Commit 713404d6 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-2.6.29' of git://linux-nfs.org/~bfields/linux

* 'for-2.6.29' of git://linux-nfs.org/~bfields/linux: (67 commits)
  nfsd: get rid of NFSD_VERSION
  nfsd: last_byte_offset
  nfsd: delete wrong file comment from nfsd/nfs4xdr.c
  nfsd: git rid of nfs4_cb_null_ops declaration
  nfsd: dprint each op status in nfsd4_proc_compound
  nfsd: add etoosmall to nfserrno
  NFSD: FIDs need to take precedence over UUIDs
  SUNRPC: The sunrpc server code should not be used by out-of-tree modules
  svc: Clean up deferred requests on transport destruction
  nfsd: fix double-locks of directory mutex
  svc: Move kfree of deferral record to common code
  CRED: Fix NFSD regression
  NLM: Clean up flow of control in make_socks() function
  NLM: Refactor make_socks() function
  nfsd: Ensure nfsv4 calls the underlying filesystem on LOCKT
  SUNRPC: Ensure the server closes sockets in a timely fashion
  NFSD: Add documenting comments for nfsctl interface
  NFSD: Replace open-coded integer with macro
  NFSD: Fix a handful of coding style issues in write_filehandle()
  NFSD: clean up failover sysctl function naming
  ...
parents d599edca db43910c
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/lockd/sm_inter.h>
#define NLMDBG_FACILITY NLMDBG_CLIENT #define NLMDBG_FACILITY NLMDBG_CLIENT
#define NLMCLNT_GRACE_WAIT (5*HZ) #define NLMCLNT_GRACE_WAIT (5*HZ)
...@@ -518,11 +517,9 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) ...@@ -518,11 +517,9 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
unsigned char fl_type; unsigned char fl_type;
int status = -ENOLCK; int status = -ENOLCK;
if (nsm_monitor(host) < 0) { if (nsm_monitor(host) < 0)
printk(KERN_NOTICE "lockd: failed to monitor %s\n",
host->h_name);
goto out; goto out;
}
fl->fl_flags |= FL_ACCESS; fl->fl_flags |= FL_ACCESS;
status = do_vfs_lock(fl); status = do_vfs_lock(fl);
fl->fl_flags = fl_flags; fl->fl_flags = fl_flags;
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/lockd/sm_inter.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <net/ipv6.h> #include <net/ipv6.h>
...@@ -32,11 +31,6 @@ static int nrhosts; ...@@ -32,11 +31,6 @@ static int nrhosts;
static DEFINE_MUTEX(nlm_host_mutex); static DEFINE_MUTEX(nlm_host_mutex);
static void nlm_gc_hosts(void); static void nlm_gc_hosts(void);
static struct nsm_handle *nsm_find(const struct sockaddr *sap,
const size_t salen,
const char *hostname,
const size_t hostname_len,
const int create);
struct nlm_lookup_host_info { struct nlm_lookup_host_info {
const int server; /* search for server|client */ const int server; /* search for server|client */
...@@ -105,32 +99,6 @@ static void nlm_clear_port(struct sockaddr *sap) ...@@ -105,32 +99,6 @@ static void nlm_clear_port(struct sockaddr *sap)
} }
} }
static void nlm_display_address(const struct sockaddr *sap,
char *buf, const size_t len)
{
const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
switch (sap->sa_family) {
case AF_UNSPEC:
snprintf(buf, len, "unspecified");
break;
case AF_INET:
snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr);
break;
case AF_INET6:
if (ipv6_addr_v4mapped(&sin6->sin6_addr))
snprintf(buf, len, "%pI4",
&sin6->sin6_addr.s6_addr32[3]);
else
snprintf(buf, len, "%pI6", &sin6->sin6_addr);
break;
default:
snprintf(buf, len, "unsupported address family");
break;
}
}
/* /*
* Common host lookup routine for server & client * Common host lookup routine for server & client
*/ */
...@@ -190,8 +158,8 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni) ...@@ -190,8 +158,8 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
atomic_inc(&nsm->sm_count); atomic_inc(&nsm->sm_count);
else { else {
host = NULL; host = NULL;
nsm = nsm_find(ni->sap, ni->salen, nsm = nsm_get_handle(ni->sap, ni->salen,
ni->hostname, ni->hostname_len, 1); ni->hostname, ni->hostname_len);
if (!nsm) { if (!nsm) {
dprintk("lockd: nlm_lookup_host failed; " dprintk("lockd: nlm_lookup_host failed; "
"no nsm handle\n"); "no nsm handle\n");
...@@ -206,6 +174,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni) ...@@ -206,6 +174,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
goto out; goto out;
} }
host->h_name = nsm->sm_name; host->h_name = nsm->sm_name;
host->h_addrbuf = nsm->sm_addrbuf;
memcpy(nlm_addr(host), ni->sap, ni->salen); memcpy(nlm_addr(host), ni->sap, ni->salen);
host->h_addrlen = ni->salen; host->h_addrlen = ni->salen;
nlm_clear_port(nlm_addr(host)); nlm_clear_port(nlm_addr(host));
...@@ -232,11 +201,6 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni) ...@@ -232,11 +201,6 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
nrhosts++; nrhosts++;
nlm_display_address((struct sockaddr *)&host->h_addr,
host->h_addrbuf, sizeof(host->h_addrbuf));
nlm_display_address((struct sockaddr *)&host->h_srcaddr,
host->h_srcaddrbuf, sizeof(host->h_srcaddrbuf));
dprintk("lockd: nlm_lookup_host created host %s\n", dprintk("lockd: nlm_lookup_host created host %s\n",
host->h_name); host->h_name);
...@@ -256,10 +220,8 @@ nlm_destroy_host(struct nlm_host *host) ...@@ -256,10 +220,8 @@ nlm_destroy_host(struct nlm_host *host)
BUG_ON(!list_empty(&host->h_lockowners)); BUG_ON(!list_empty(&host->h_lockowners));
BUG_ON(atomic_read(&host->h_count)); BUG_ON(atomic_read(&host->h_count));
/*
* Release NSM handle and unmonitor host.
*/
nsm_unmonitor(host); nsm_unmonitor(host);
nsm_release(host->h_nsmhandle);
clnt = host->h_rpcclnt; clnt = host->h_rpcclnt;
if (clnt != NULL) if (clnt != NULL)
...@@ -378,8 +340,8 @@ nlm_bind_host(struct nlm_host *host) ...@@ -378,8 +340,8 @@ nlm_bind_host(struct nlm_host *host)
{ {
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
dprintk("lockd: nlm_bind_host %s (%s), my addr=%s\n", dprintk("lockd: nlm_bind_host %s (%s)\n",
host->h_name, host->h_addrbuf, host->h_srcaddrbuf); host->h_name, host->h_addrbuf);
/* Lock host handle */ /* Lock host handle */
mutex_lock(&host->h_mutex); mutex_lock(&host->h_mutex);
...@@ -481,35 +443,23 @@ void nlm_release_host(struct nlm_host *host) ...@@ -481,35 +443,23 @@ void nlm_release_host(struct nlm_host *host)
} }
} }
/* /**
* We were notified that the host indicated by address &sin * nlm_host_rebooted - Release all resources held by rebooted host
* has rebooted. * @info: pointer to decoded results of NLM_SM_NOTIFY call
* Release all resources held by that peer. *
* We were notified that the specified host has rebooted. Release
* all resources held by that peer.
*/ */
void nlm_host_rebooted(const struct sockaddr_in *sin, void nlm_host_rebooted(const struct nlm_reboot *info)
const char *hostname,
unsigned int hostname_len,
u32 new_state)
{ {
struct hlist_head *chain; struct hlist_head *chain;
struct hlist_node *pos; struct hlist_node *pos;
struct nsm_handle *nsm; struct nsm_handle *nsm;
struct nlm_host *host; struct nlm_host *host;
nsm = nsm_find((struct sockaddr *)sin, sizeof(*sin), nsm = nsm_reboot_lookup(info);
hostname, hostname_len, 0); if (unlikely(nsm == NULL))
if (nsm == NULL) {
dprintk("lockd: never saw rebooted peer '%.*s' before\n",
hostname_len, hostname);
return; return;
}
dprintk("lockd: nlm_host_rebooted(%.*s, %s)\n",
hostname_len, hostname, nsm->sm_addrbuf);
/* When reclaiming locks on this peer, make sure that
* we set up a new notification */
nsm->sm_monitored = 0;
/* Mark all hosts tied to this NSM state as having rebooted. /* Mark all hosts tied to this NSM state as having rebooted.
* We run the loop repeatedly, because we drop the host table * We run the loop repeatedly, because we drop the host table
...@@ -520,8 +470,8 @@ again: mutex_lock(&nlm_host_mutex); ...@@ -520,8 +470,8 @@ again: mutex_lock(&nlm_host_mutex);
for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
hlist_for_each_entry(host, pos, chain, h_hash) { hlist_for_each_entry(host, pos, chain, h_hash) {
if (host->h_nsmhandle == nsm if (host->h_nsmhandle == nsm
&& host->h_nsmstate != new_state) { && host->h_nsmstate != info->state) {
host->h_nsmstate = new_state; host->h_nsmstate = info->state;
host->h_state++; host->h_state++;
nlm_get_host(host); nlm_get_host(host);
...@@ -629,89 +579,3 @@ nlm_gc_hosts(void) ...@@ -629,89 +579,3 @@ nlm_gc_hosts(void)
next_gc = jiffies + NLM_HOST_COLLECT; next_gc = jiffies + NLM_HOST_COLLECT;
} }
/*
* Manage NSM handles
*/
static LIST_HEAD(nsm_handles);
static DEFINE_SPINLOCK(nsm_lock);
static struct nsm_handle *nsm_find(const struct sockaddr *sap,
const size_t salen,
const char *hostname,
const size_t hostname_len,
const int create)
{
struct nsm_handle *nsm = NULL;
struct nsm_handle *pos;
if (!sap)
return NULL;
if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
if (printk_ratelimit()) {
printk(KERN_WARNING "Invalid hostname \"%.*s\" "
"in NFS lock request\n",
(int)hostname_len, hostname);
}
return NULL;
}
retry:
spin_lock(&nsm_lock);
list_for_each_entry(pos, &nsm_handles, sm_link) {
if (hostname && nsm_use_hostnames) {
if (strlen(pos->sm_name) != hostname_len
|| memcmp(pos->sm_name, hostname, hostname_len))
continue;
} else if (!nlm_cmp_addr(nsm_addr(pos), sap))
continue;
atomic_inc(&pos->sm_count);
kfree(nsm);
nsm = pos;
goto found;
}
if (nsm) {
list_add(&nsm->sm_link, &nsm_handles);
goto found;
}
spin_unlock(&nsm_lock);
if (!create)
return NULL;
nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
if (nsm == NULL)
return NULL;
memcpy(nsm_addr(nsm), sap, salen);
nsm->sm_addrlen = salen;
nsm->sm_name = (char *) (nsm + 1);
memcpy(nsm->sm_name, hostname, hostname_len);
nsm->sm_name[hostname_len] = '\0';
nlm_display_address((struct sockaddr *)&nsm->sm_addr,
nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf));
atomic_set(&nsm->sm_count, 1);
goto retry;
found:
spin_unlock(&nsm_lock);
return nsm;
}
/*
* Release an NSM handle
*/
void
nsm_release(struct nsm_handle *nsm)
{
if (!nsm)
return;
if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
list_del(&nsm->sm_link);
spin_unlock(&nsm_lock);
kfree(nsm);
}
}
...@@ -9,35 +9,123 @@ ...@@ -9,35 +9,123 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/utsname.h> #include <linux/utsname.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xprtsock.h> #include <linux/sunrpc/xprtsock.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/lockd/sm_inter.h>
#define NLMDBG_FACILITY NLMDBG_MONITOR #define NLMDBG_FACILITY NLMDBG_MONITOR
#define NSM_PROGRAM 100024
#define NSM_VERSION 1
enum {
NSMPROC_NULL,
NSMPROC_STAT,
NSMPROC_MON,
NSMPROC_UNMON,
NSMPROC_UNMON_ALL,
NSMPROC_SIMU_CRASH,
NSMPROC_NOTIFY,
};
struct nsm_args {
struct nsm_private *priv;
u32 prog; /* RPC callback info */
u32 vers;
u32 proc;
#define XDR_ADDRBUF_LEN (20) char *mon_name;
};
static struct rpc_clnt * nsm_create(void); struct nsm_res {
u32 status;
u32 state;
};
static struct rpc_program nsm_program; static struct rpc_program nsm_program;
static LIST_HEAD(nsm_handles);
static DEFINE_SPINLOCK(nsm_lock);
/* /*
* Local NSM state * Local NSM state
*/ */
int nsm_local_state; int __read_mostly nsm_local_state;
int __read_mostly nsm_use_hostnames;
/* static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm)
* Common procedure for SM_MON/SM_UNMON calls {
*/ return (struct sockaddr *)&nsm->sm_addr;
static int }
nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
static void nsm_display_ipv4_address(const struct sockaddr *sap, char *buf,
const size_t len)
{
const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr);
}
static void nsm_display_ipv6_address(const struct sockaddr *sap, char *buf,
const size_t len)
{
const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
if (ipv6_addr_v4mapped(&sin6->sin6_addr))
snprintf(buf, len, "%pI4", &sin6->sin6_addr.s6_addr32[3]);
else if (sin6->sin6_scope_id != 0)
snprintf(buf, len, "%pI6%%%u", &sin6->sin6_addr,
sin6->sin6_scope_id);
else
snprintf(buf, len, "%pI6", &sin6->sin6_addr);
}
static void nsm_display_address(const struct sockaddr *sap,
char *buf, const size_t len)
{
switch (sap->sa_family) {
case AF_INET:
nsm_display_ipv4_address(sap, buf, len);
break;
case AF_INET6:
nsm_display_ipv6_address(sap, buf, len);
break;
default:
snprintf(buf, len, "unsupported address family");
break;
}
}
static struct rpc_clnt *nsm_create(void)
{
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
struct rpc_create_args args = {
.protocol = XPRT_TRANSPORT_UDP,
.address = (struct sockaddr *)&sin,
.addrsize = sizeof(sin),
.servername = "rpc.statd",
.program = &nsm_program,
.version = NSM_VERSION,
.authflavor = RPC_AUTH_NULL,
};
return rpc_create(&args);
}
static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
{ {
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
int status; int status;
struct nsm_args args; struct nsm_args args = {
.priv = &nsm->sm_priv,
.prog = NLM_PROGRAM,
.vers = 3,
.proc = NLMPROC_NSM_NOTIFY,
.mon_name = nsm->sm_mon_name,
};
struct rpc_message msg = { struct rpc_message msg = {
.rpc_argp = &args, .rpc_argp = &args,
.rpc_resp = res, .rpc_resp = res,
...@@ -46,22 +134,18 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) ...@@ -46,22 +134,18 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
clnt = nsm_create(); clnt = nsm_create();
if (IS_ERR(clnt)) { if (IS_ERR(clnt)) {
status = PTR_ERR(clnt); status = PTR_ERR(clnt);
dprintk("lockd: failed to create NSM upcall transport, "
"status=%d\n", status);
goto out; goto out;
} }
memset(&args, 0, sizeof(args));
args.mon_name = nsm->sm_name;
args.addr = nsm_addr_in(nsm)->sin_addr.s_addr;
args.prog = NLM_PROGRAM;
args.vers = 3;
args.proc = NLMPROC_NSM_NOTIFY;
memset(res, 0, sizeof(*res)); memset(res, 0, sizeof(*res));
msg.rpc_proc = &clnt->cl_procinfo[proc]; msg.rpc_proc = &clnt->cl_procinfo[proc];
status = rpc_call_sync(clnt, &msg, 0); status = rpc_call_sync(clnt, &msg, 0);
if (status < 0) if (status < 0)
printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n", dprintk("lockd: NSM upcall RPC failed, status=%d\n",
status); status);
else else
status = 0; status = 0;
rpc_shutdown_client(clnt); rpc_shutdown_client(clnt);
...@@ -69,82 +153,272 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) ...@@ -69,82 +153,272 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
return status; return status;
} }
/* /**
* Set up monitoring of a remote host * nsm_monitor - Notify a peer in case we reboot
* @host: pointer to nlm_host of peer to notify
*
* If this peer is not already monitored, this function sends an
* upcall to the local rpc.statd to record the name/address of
* the peer to notify in case we reboot.
*
* Returns zero if the peer is monitored by the local rpc.statd;
* otherwise a negative errno value is returned.
*/ */
int int nsm_monitor(const struct nlm_host *host)
nsm_monitor(struct nlm_host *host)
{ {
struct nsm_handle *nsm = host->h_nsmhandle; struct nsm_handle *nsm = host->h_nsmhandle;
struct nsm_res res; struct nsm_res res;
int status; int status;
dprintk("lockd: nsm_monitor(%s)\n", host->h_name); dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name);
BUG_ON(nsm == NULL);
if (nsm->sm_monitored) if (nsm->sm_monitored)
return 0; return 0;
status = nsm_mon_unmon(nsm, SM_MON, &res); /*
* Choose whether to record the caller_name or IP address of
* this peer in the local rpc.statd's database.
*/
nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
if (status < 0 || res.status != 0) status = nsm_mon_unmon(nsm, NSMPROC_MON, &res);
printk(KERN_NOTICE "lockd: cannot monitor %s\n", host->h_name); if (res.status != 0)
status = -EIO;
if (status < 0)
printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name);
else else
nsm->sm_monitored = 1; nsm->sm_monitored = 1;
return status; return status;
} }
/* /**
* Cease to monitor remote host * nsm_unmonitor - Unregister peer notification
* @host: pointer to nlm_host of peer to stop monitoring
*
* If this peer is monitored, this function sends an upcall to
* tell the local rpc.statd not to send this peer a notification
* when we reboot.
*/ */
int void nsm_unmonitor(const struct nlm_host *host)
nsm_unmonitor(struct nlm_host *host)
{ {
struct nsm_handle *nsm = host->h_nsmhandle; struct nsm_handle *nsm = host->h_nsmhandle;
struct nsm_res res; struct nsm_res res;
int status = 0; int status;
if (nsm == NULL)
return 0;
host->h_nsmhandle = NULL;
if (atomic_read(&nsm->sm_count) == 1 if (atomic_read(&nsm->sm_count) == 1
&& nsm->sm_monitored && !nsm->sm_sticky) { && nsm->sm_monitored && !nsm->sm_sticky) {
dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name); dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name);
status = nsm_mon_unmon(nsm, SM_UNMON, &res); status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res);
if (res.status != 0)
status = -EIO;
if (status < 0) if (status < 0)
printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", printk(KERN_NOTICE "lockd: cannot unmonitor %s\n",
host->h_name); nsm->sm_name);
else else
nsm->sm_monitored = 0; nsm->sm_monitored = 0;
} }
nsm_release(nsm); }
return status;
static struct nsm_handle *nsm_lookup_hostname(const char *hostname,
const size_t len)
{
struct nsm_handle *nsm;
list_for_each_entry(nsm, &nsm_handles, sm_link)
if (strlen(nsm->sm_name) == len &&
memcmp(nsm->sm_name, hostname, len) == 0)
return nsm;
return NULL;
}
static struct nsm_handle *nsm_lookup_addr(const struct sockaddr *sap)
{
struct nsm_handle *nsm;
list_for_each_entry(nsm, &nsm_handles, sm_link)
if (nlm_cmp_addr(nsm_addr(nsm), sap))
return nsm;
return NULL;
}
static struct nsm_handle *nsm_lookup_priv(const struct nsm_private *priv)
{
struct nsm_handle *nsm;
list_for_each_entry(nsm, &nsm_handles, sm_link)
if (memcmp(nsm->sm_priv.data, priv->data,
sizeof(priv->data)) == 0)
return nsm;
return NULL;
} }
/* /*
* Create NSM client for the local host * Construct a unique cookie to match this nsm_handle to this monitored
* host. It is passed to the local rpc.statd via NSMPROC_MON, and
* returned via NLMPROC_SM_NOTIFY, in the "priv" field of these
* requests.
*
* The NSM protocol requires that these cookies be unique while the
* system is running. We prefer a stronger requirement of making them
* unique across reboots. If user space bugs cause a stale cookie to
* be sent to the kernel, it could cause the wrong host to lose its
* lock state if cookies were not unique across reboots.
*
* The cookies are exposed only to local user space via loopback. They
* do not appear on the physical network. If we want greater security
* for some reason, nsm_init_private() could perform a one-way hash to
* obscure the contents of the cookie.
*/ */
static struct rpc_clnt * static void nsm_init_private(struct nsm_handle *nsm)
nsm_create(void)
{ {
struct sockaddr_in sin = { u64 *p = (u64 *)&nsm->sm_priv.data;
.sin_family = AF_INET, struct timespec ts;
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
.sin_port = 0,
};
struct rpc_create_args args = {
.protocol = XPRT_TRANSPORT_UDP,
.address = (struct sockaddr *)&sin,
.addrsize = sizeof(sin),
.servername = "localhost",
.program = &nsm_program,
.version = SM_VERSION,
.authflavor = RPC_AUTH_NULL,
};
return rpc_create(&args); ktime_get_ts(&ts);
*p++ = timespec_to_ns(&ts);
*p = (unsigned long)nsm;
}
static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
const size_t salen,
const char *hostname,
const size_t hostname_len)
{
struct nsm_handle *new;
new = kzalloc(sizeof(*new) + hostname_len + 1, GFP_KERNEL);
if (unlikely(new == NULL))
return NULL;
atomic_set(&new->sm_count, 1);
new->sm_name = (char *)(new + 1);
memcpy(nsm_addr(new), sap, salen);
new->sm_addrlen = salen;
nsm_init_private(new);
nsm_display_address((const struct sockaddr *)&new->sm_addr,
new->sm_addrbuf, sizeof(new->sm_addrbuf));
memcpy(new->sm_name, hostname, hostname_len);
new->sm_name[hostname_len] = '\0';
return new;
}
/**
* nsm_get_handle - Find or create a cached nsm_handle
* @sap: pointer to socket address of handle to find
* @salen: length of socket address
* @hostname: pointer to C string containing hostname to find
* @hostname_len: length of C string
*
* Behavior is modulated by the global nsm_use_hostnames variable.
*
* Returns a cached nsm_handle after bumping its ref count, or
* returns a fresh nsm_handle if a handle that matches @sap and/or
* @hostname cannot be found in the handle cache. Returns NULL if
* an error occurs.
*/
struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,
const size_t salen, const char *hostname,
const size_t hostname_len)
{
struct nsm_handle *cached, *new = NULL;
if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
if (printk_ratelimit()) {
printk(KERN_WARNING "Invalid hostname \"%.*s\" "
"in NFS lock request\n",
(int)hostname_len, hostname);
}
return NULL;
}
retry:
spin_lock(&nsm_lock);
if (nsm_use_hostnames && hostname != NULL)
cached = nsm_lookup_hostname(hostname, hostname_len);
else
cached = nsm_lookup_addr(sap);
if (cached != NULL) {
atomic_inc(&cached->sm_count);
spin_unlock(&nsm_lock);
kfree(new);
dprintk("lockd: found nsm_handle for %s (%s), "
"cnt %d\n", cached->sm_name,
cached->sm_addrbuf,
atomic_read(&cached->sm_count));
return cached;
}
if (new != NULL) {
list_add(&new->sm_link, &nsm_handles);
spin_unlock(&nsm_lock);
dprintk("lockd: created nsm_handle for %s (%s)\n",
new->sm_name, new->sm_addrbuf);
return new;
}
spin_unlock(&nsm_lock);
new = nsm_create_handle(sap, salen, hostname, hostname_len);
if (unlikely(new == NULL))
return NULL;
goto retry;
}
/**
* nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle
* @info: pointer to NLMPROC_SM_NOTIFY arguments
*
* Returns a matching nsm_handle if found in the nsm cache; the returned
* nsm_handle's reference count is bumped and sm_monitored is cleared.
* Otherwise returns NULL if some error occurred.
*/
struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info)
{
struct nsm_handle *cached;
spin_lock(&nsm_lock);
cached = nsm_lookup_priv(&info->priv);
if (unlikely(cached == NULL)) {
spin_unlock(&nsm_lock);
dprintk("lockd: never saw rebooted peer '%.*s' before\n",
info->len, info->mon);
return cached;
}
atomic_inc(&cached->sm_count);
spin_unlock(&nsm_lock);
/*
* During subsequent lock activity, force a fresh
* notification to be set up for this host.
*/
cached->sm_monitored = 0;
dprintk("lockd: host %s (%s) rebooted, cnt %d\n",
cached->sm_name, cached->sm_addrbuf,
atomic_read(&cached->sm_count));
return cached;
}
/**
* nsm_release - Release an NSM handle
* @nsm: pointer to handle to be released
*
*/
void nsm_release(struct nsm_handle *nsm)
{
if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
list_del(&nsm->sm_link);
spin_unlock(&nsm_lock);
dprintk("lockd: destroyed nsm_handle for %s (%s)\n",
nsm->sm_name, nsm->sm_addrbuf);
kfree(nsm);
}
} }
/* /*
...@@ -154,127 +428,132 @@ nsm_create(void) ...@@ -154,127 +428,132 @@ nsm_create(void)
* Status Monitor wire protocol. * Status Monitor wire protocol.
*/ */
static __be32 *xdr_encode_nsm_string(__be32 *p, char *string) static int encode_nsm_string(struct xdr_stream *xdr, const char *string)
{ {
size_t len = strlen(string); const u32 len = strlen(string);
__be32 *p;
if (len > SM_MAXSTRLEN)
len = SM_MAXSTRLEN; if (unlikely(len > SM_MAXSTRLEN))
return xdr_encode_opaque(p, string, len); return -EIO;
p = xdr_reserve_space(xdr, sizeof(u32) + len);
if (unlikely(p == NULL))
return -EIO;
xdr_encode_opaque(p, string, len);
return 0;
} }
/* /*
* "mon_name" specifies the host to be monitored. * "mon_name" specifies the host to be monitored.
*
* Linux uses a text version of the IP address of the remote
* host as the host identifier (the "mon_name" argument).
*
* Linux statd always looks up the canonical hostname first for
* whatever remote hostname it receives, so this works alright.
*/ */
static __be32 *xdr_encode_mon_name(__be32 *p, struct nsm_args *argp) static int encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
{ {
char buffer[XDR_ADDRBUF_LEN + 1]; return encode_nsm_string(xdr, argp->mon_name);
char *name = argp->mon_name;
if (!nsm_use_hostnames) {
snprintf(buffer, XDR_ADDRBUF_LEN,
"%pI4", &argp->addr);
name = buffer;
}
return xdr_encode_nsm_string(p, name);
} }
/* /*
* The "my_id" argument specifies the hostname and RPC procedure * The "my_id" argument specifies the hostname and RPC procedure
* to be called when the status manager receives notification * to be called when the status manager receives notification
* (via the SM_NOTIFY call) that the state of host "mon_name" * (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name"
* has changed. * has changed.
*/ */
static __be32 *xdr_encode_my_id(__be32 *p, struct nsm_args *argp) static int encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
{ {
p = xdr_encode_nsm_string(p, utsname()->nodename); int status;
if (!p) __be32 *p;
return ERR_PTR(-EIO);
status = encode_nsm_string(xdr, utsname()->nodename);
if (unlikely(status != 0))
return status;
p = xdr_reserve_space(xdr, 3 * sizeof(u32));
if (unlikely(p == NULL))
return -EIO;
*p++ = htonl(argp->prog); *p++ = htonl(argp->prog);
*p++ = htonl(argp->vers); *p++ = htonl(argp->vers);
*p++ = htonl(argp->proc); *p++ = htonl(argp->proc);
return 0;
return p;
} }
/* /*
* The "mon_id" argument specifies the non-private arguments * The "mon_id" argument specifies the non-private arguments
* of an SM_MON or SM_UNMON call. * of an NSMPROC_MON or NSMPROC_UNMON call.
*/ */
static __be32 *xdr_encode_mon_id(__be32 *p, struct nsm_args *argp) static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
{ {
p = xdr_encode_mon_name(p, argp); int status;
if (!p)
return ERR_PTR(-EIO);
return xdr_encode_my_id(p, argp); status = encode_mon_name(xdr, argp);
if (unlikely(status != 0))
return status;
return encode_my_id(xdr, argp);
} }
/* /*
* The "priv" argument may contain private information required * The "priv" argument may contain private information required
* by the SM_MON call. This information will be supplied in the * by the NSMPROC_MON call. This information will be supplied in the
* SM_NOTIFY call. * NLMPROC_SM_NOTIFY call.
*
* Linux provides the raw IP address of the monitored host,
* left in network byte order.
*/ */
static __be32 *xdr_encode_priv(__be32 *p, struct nsm_args *argp) static int encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp)
{ {
*p++ = argp->addr; __be32 *p;
*p++ = 0;
*p++ = 0;
*p++ = 0;
return p; p = xdr_reserve_space(xdr, SM_PRIV_SIZE);
if (unlikely(p == NULL))
return -EIO;
xdr_encode_opaque_fixed(p, argp->priv->data, SM_PRIV_SIZE);
return 0;
} }
static int static int xdr_enc_mon(struct rpc_rqst *req, __be32 *p,
xdr_encode_mon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp) const struct nsm_args *argp)
{ {
p = xdr_encode_mon_id(p, argp); struct xdr_stream xdr;
if (IS_ERR(p)) int status;
return PTR_ERR(p);
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
p = xdr_encode_priv(p, argp); status = encode_mon_id(&xdr, argp);
if (IS_ERR(p)) if (unlikely(status))
return PTR_ERR(p); return status;
return encode_priv(&xdr, argp);
rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
return 0;
} }
static int static int xdr_enc_unmon(struct rpc_rqst *req, __be32 *p,
xdr_encode_unmon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp) const struct nsm_args *argp)
{ {
p = xdr_encode_mon_id(p, argp); struct xdr_stream xdr;
if (IS_ERR(p))
return PTR_ERR(p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p); return encode_mon_id(&xdr, argp);
return 0;
} }
static int static int xdr_dec_stat_res(struct rpc_rqst *rqstp, __be32 *p,
xdr_decode_stat_res(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp) struct nsm_res *resp)
{ {
struct xdr_stream xdr;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
p = xdr_inline_decode(&xdr, 2 * sizeof(u32));
if (unlikely(p == NULL))
return -EIO;
resp->status = ntohl(*p++); resp->status = ntohl(*p++);
resp->state = ntohl(*p++); resp->state = ntohl(*p);
dprintk("nsm: xdr_decode_stat_res status %d state %d\n",
dprintk("lockd: xdr_dec_stat_res status %d state %d\n",
resp->status, resp->state); resp->status, resp->state);
return 0; return 0;
} }
static int static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p,
xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp) struct nsm_res *resp)
{ {
resp->state = ntohl(*p++); struct xdr_stream xdr;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
p = xdr_inline_decode(&xdr, sizeof(u32));
if (unlikely(p == NULL))
return -EIO;
resp->state = ntohl(*p);
dprintk("lockd: xdr_dec_stat state %d\n", resp->state);
return 0; return 0;
} }
...@@ -288,22 +567,22 @@ xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp) ...@@ -288,22 +567,22 @@ xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
#define SM_unmonres_sz 1 #define SM_unmonres_sz 1
static struct rpc_procinfo nsm_procedures[] = { static struct rpc_procinfo nsm_procedures[] = {
[SM_MON] = { [NSMPROC_MON] = {
.p_proc = SM_MON, .p_proc = NSMPROC_MON,
.p_encode = (kxdrproc_t) xdr_encode_mon, .p_encode = (kxdrproc_t)xdr_enc_mon,
.p_decode = (kxdrproc_t) xdr_decode_stat_res, .p_decode = (kxdrproc_t)xdr_dec_stat_res,
.p_arglen = SM_mon_sz, .p_arglen = SM_mon_sz,
.p_replen = SM_monres_sz, .p_replen = SM_monres_sz,
.p_statidx = SM_MON, .p_statidx = NSMPROC_MON,
.p_name = "MONITOR", .p_name = "MONITOR",
}, },
[SM_UNMON] = { [NSMPROC_UNMON] = {
.p_proc = SM_UNMON, .p_proc = NSMPROC_UNMON,
.p_encode = (kxdrproc_t) xdr_encode_unmon, .p_encode = (kxdrproc_t)xdr_enc_unmon,
.p_decode = (kxdrproc_t) xdr_decode_stat, .p_decode = (kxdrproc_t)xdr_dec_stat,
.p_arglen = SM_mon_id_sz, .p_arglen = SM_mon_id_sz,
.p_replen = SM_unmonres_sz, .p_replen = SM_unmonres_sz,
.p_statidx = SM_UNMON, .p_statidx = NSMPROC_UNMON,
.p_name = "UNMONITOR", .p_name = "UNMONITOR",
}, },
}; };
...@@ -322,7 +601,7 @@ static struct rpc_stat nsm_stats; ...@@ -322,7 +601,7 @@ static struct rpc_stat nsm_stats;
static struct rpc_program nsm_program = { static struct rpc_program nsm_program = {
.name = "statd", .name = "statd",
.number = SM_PROGRAM, .number = NSM_PROGRAM,
.nrvers = ARRAY_SIZE(nsm_version), .nrvers = ARRAY_SIZE(nsm_version),
.version = nsm_version, .version = nsm_version,
.stats = &nsm_stats .stats = &nsm_stats
......
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
#include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/svcsock.h>
#include <net/ip.h> #include <net/ip.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/lockd/sm_inter.h>
#include <linux/nfs.h> #include <linux/nfs.h>
#define NLMDBG_FACILITY NLMDBG_SVC #define NLMDBG_FACILITY NLMDBG_SVC
...@@ -53,6 +52,17 @@ static struct task_struct *nlmsvc_task; ...@@ -53,6 +52,17 @@ static struct task_struct *nlmsvc_task;
static struct svc_rqst *nlmsvc_rqst; static struct svc_rqst *nlmsvc_rqst;
unsigned long nlmsvc_timeout; unsigned long nlmsvc_timeout;
/*
* If the kernel has IPv6 support available, always listen for
* both AF_INET and AF_INET6 requests.
*/
#if (defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)) && \
defined(CONFIG_SUNRPC_REGISTER_V4)
static const sa_family_t nlmsvc_family = AF_INET6;
#else /* (CONFIG_IPV6 || CONFIG_IPV6_MODULE) && CONFIG_SUNRPC_REGISTER_V4 */
static const sa_family_t nlmsvc_family = AF_INET;
#endif /* (CONFIG_IPV6 || CONFIG_IPV6_MODULE) && CONFIG_SUNRPC_REGISTER_V4 */
/* /*
* These can be set at insmod time (useful for NFS as root filesystem), * These can be set at insmod time (useful for NFS as root filesystem),
* and also changed through the sysctl interface. -- Jamie Lokier, Aug 2003 * and also changed through the sysctl interface. -- Jamie Lokier, Aug 2003
...@@ -60,7 +70,9 @@ unsigned long nlmsvc_timeout; ...@@ -60,7 +70,9 @@ unsigned long nlmsvc_timeout;
static unsigned long nlm_grace_period; static unsigned long nlm_grace_period;
static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO;
static int nlm_udpport, nlm_tcpport; static int nlm_udpport, nlm_tcpport;
int nsm_use_hostnames = 0;
/* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */
static unsigned int nlm_max_connections = 1024;
/* /*
* Constants needed for the sysctl interface. * Constants needed for the sysctl interface.
...@@ -143,6 +155,9 @@ lockd(void *vrqstp) ...@@ -143,6 +155,9 @@ lockd(void *vrqstp)
long timeout = MAX_SCHEDULE_TIMEOUT; long timeout = MAX_SCHEDULE_TIMEOUT;
RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
/* update sv_maxconn if it has changed */
rqstp->rq_server->sv_maxconn = nlm_max_connections;
if (signalled()) { if (signalled()) {
flush_signals(current); flush_signals(current);
if (nlmsvc_ops) { if (nlmsvc_ops) {
...@@ -189,6 +204,19 @@ lockd(void *vrqstp) ...@@ -189,6 +204,19 @@ lockd(void *vrqstp)
return 0; return 0;
} }
static int create_lockd_listener(struct svc_serv *serv, char *name,
unsigned short port)
{
struct svc_xprt *xprt;
xprt = svc_find_xprt(serv, name, 0, 0);
if (xprt == NULL)
return svc_create_xprt(serv, name, port, SVC_SOCK_DEFAULTS);
svc_xprt_put(xprt);
return 0;
}
/* /*
* Ensure there are active UDP and TCP listeners for lockd. * Ensure there are active UDP and TCP listeners for lockd.
* *
...@@ -202,29 +230,23 @@ lockd(void *vrqstp) ...@@ -202,29 +230,23 @@ lockd(void *vrqstp)
static int make_socks(struct svc_serv *serv) static int make_socks(struct svc_serv *serv)
{ {
static int warned; static int warned;
struct svc_xprt *xprt; int err;
int err = 0;
xprt = svc_find_xprt(serv, "udp", 0, 0); err = create_lockd_listener(serv, "udp", nlm_udpport);
if (!xprt) if (err < 0)
err = svc_create_xprt(serv, "udp", nlm_udpport, goto out_err;
SVC_SOCK_DEFAULTS);
else err = create_lockd_listener(serv, "tcp", nlm_tcpport);
svc_xprt_put(xprt); if (err < 0)
if (err >= 0) { goto out_err;
xprt = svc_find_xprt(serv, "tcp", 0, 0);
if (!xprt) warned = 0;
err = svc_create_xprt(serv, "tcp", nlm_tcpport, return 0;
SVC_SOCK_DEFAULTS);
else out_err:
svc_xprt_put(xprt); if (warned++ == 0)
}
if (err >= 0) {
warned = 0;
err = 0;
} else if (warned++ == 0)
printk(KERN_WARNING printk(KERN_WARNING
"lockd_up: makesock failed, error=%d\n", err); "lockd_up: makesock failed, error=%d\n", err);
return err; return err;
} }
...@@ -252,7 +274,7 @@ int lockd_up(void) ...@@ -252,7 +274,7 @@ int lockd_up(void)
"lockd_up: no pid, %d users??\n", nlmsvc_users); "lockd_up: no pid, %d users??\n", nlmsvc_users);
error = -ENOMEM; error = -ENOMEM;
serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, AF_INET, NULL); serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, nlmsvc_family, NULL);
if (!serv) { if (!serv) {
printk(KERN_WARNING "lockd_up: create service failed\n"); printk(KERN_WARNING "lockd_up: create service failed\n");
goto out; goto out;
...@@ -276,6 +298,7 @@ int lockd_up(void) ...@@ -276,6 +298,7 @@ int lockd_up(void)
} }
svc_sock_update_bufs(serv); svc_sock_update_bufs(serv);
serv->sv_maxconn = nlm_max_connections;
nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, serv->sv_name); nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, serv->sv_name);
if (IS_ERR(nlmsvc_task)) { if (IS_ERR(nlmsvc_task)) {
...@@ -485,6 +508,7 @@ module_param_call(nlm_udpport, param_set_port, param_get_int, ...@@ -485,6 +508,7 @@ module_param_call(nlm_udpport, param_set_port, param_get_int,
module_param_call(nlm_tcpport, param_set_port, param_get_int, module_param_call(nlm_tcpport, param_set_port, param_get_int,
&nlm_tcpport, 0644); &nlm_tcpport, 0644);
module_param(nsm_use_hostnames, bool, 0644); module_param(nsm_use_hostnames, bool, 0644);
module_param(nlm_max_connections, uint, 0644);
/* /*
* Initialising and terminating the module. * Initialising and terminating the module.
......
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
#include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsd.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/lockd/share.h> #include <linux/lockd/share.h>
#include <linux/lockd/sm_inter.h>
#define NLMDBG_FACILITY NLMDBG_CLIENT #define NLMDBG_FACILITY NLMDBG_CLIENT
...@@ -419,8 +417,6 @@ static __be32 ...@@ -419,8 +417,6 @@ static __be32
nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
void *resp) void *resp)
{ {
struct sockaddr_in saddr;
dprintk("lockd: SM_NOTIFY called\n"); dprintk("lockd: SM_NOTIFY called\n");
if (!nlm_privileged_requester(rqstp)) { if (!nlm_privileged_requester(rqstp)) {
...@@ -430,14 +426,7 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, ...@@ -430,14 +426,7 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
return rpc_system_err; return rpc_system_err;
} }
/* Obtain the host pointer for this NFS server and try to nlm_host_rebooted(argp);
* reclaim all locks we hold on this server.
*/
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = argp->addr;
nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state);
return rpc_success; return rpc_success;
} }
......
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
#include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsd.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/lockd/share.h> #include <linux/lockd/share.h>
#include <linux/lockd/sm_inter.h>
#define NLMDBG_FACILITY NLMDBG_CLIENT #define NLMDBG_FACILITY NLMDBG_CLIENT
...@@ -451,8 +449,6 @@ static __be32 ...@@ -451,8 +449,6 @@ static __be32
nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
void *resp) void *resp)
{ {
struct sockaddr_in saddr;
dprintk("lockd: SM_NOTIFY called\n"); dprintk("lockd: SM_NOTIFY called\n");
if (!nlm_privileged_requester(rqstp)) { if (!nlm_privileged_requester(rqstp)) {
...@@ -462,14 +458,7 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, ...@@ -462,14 +458,7 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
return rpc_system_err; return rpc_system_err;
} }
/* Obtain the host pointer for this NFS server and try to nlm_host_rebooted(argp);
* reclaim all locks we hold on this server.
*/
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = argp->addr;
nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state);
return rpc_success; return rpc_success;
} }
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include <linux/nfsd/export.h> #include <linux/nfsd/export.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/lockd/share.h> #include <linux/lockd/share.h>
#include <linux/lockd/sm_inter.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mount.h> #include <linux/mount.h>
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/lockd/sm_inter.h>
#define NLMDBG_FACILITY NLMDBG_XDR #define NLMDBG_FACILITY NLMDBG_XDR
...@@ -349,8 +348,8 @@ nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp) ...@@ -349,8 +348,8 @@ nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp)
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN))) if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
return 0; return 0;
argp->state = ntohl(*p++); argp->state = ntohl(*p++);
/* Preserve the address in network byte order */ memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
argp->addr = *p++; p += XDR_QUADLEN(SM_PRIV_SIZE);
return xdr_argsize_check(rqstp, p); return xdr_argsize_check(rqstp, p);
} }
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/lockd/sm_inter.h>
#define NLMDBG_FACILITY NLMDBG_XDR #define NLMDBG_FACILITY NLMDBG_XDR
...@@ -356,8 +355,8 @@ nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp ...@@ -356,8 +355,8 @@ nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN))) if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
return 0; return 0;
argp->state = ntohl(*p++); argp->state = ntohl(*p++);
/* Preserve the address in network byte order */ memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
argp->addr = *p++; p += XDR_QUADLEN(SM_PRIV_SIZE);
return xdr_argsize_check(rqstp, p); return xdr_argsize_check(rqstp, p);
} }
......
...@@ -76,10 +76,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) ...@@ -76,10 +76,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
ret = set_groups(new, gi); ret = set_groups(new, gi);
put_group_info(gi); put_group_info(gi);
if (!ret) if (ret < 0)
goto error; goto error;
if (new->uid) if (new->fsuid)
new->cap_effective = cap_drop_nfsd_set(new->cap_effective); new->cap_effective = cap_drop_nfsd_set(new->cap_effective);
else else
new->cap_effective = cap_raise_nfsd_set(new->cap_effective, new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
......
...@@ -53,9 +53,6 @@ ...@@ -53,9 +53,6 @@
#define NFSPROC4_CB_NULL 0 #define NFSPROC4_CB_NULL 0
#define NFSPROC4_CB_COMPOUND 1 #define NFSPROC4_CB_COMPOUND 1
/* declarations */
static const struct rpc_call_ops nfs4_cb_null_ops;
/* Index of predefined Linux callback client operations */ /* Index of predefined Linux callback client operations */
enum { enum {
......
...@@ -946,6 +946,11 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -946,6 +946,11 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
nfsd4_encode_operation(resp, op); nfsd4_encode_operation(resp, op);
status = op->status; status = op->status;
} }
dprintk("nfsv4 compound op %p opcnt %d #%d: %d: status %d\n",
args->ops, args->opcnt, resp->opcnt, op->opnum,
be32_to_cpu(status));
if (cstate->replay_owner) { if (cstate->replay_owner) {
nfs4_put_stateowner(cstate->replay_owner); nfs4_put_stateowner(cstate->replay_owner);
cstate->replay_owner = NULL; cstate->replay_owner = NULL;
......
...@@ -116,9 +116,9 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) ...@@ -116,9 +116,9 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname)
md5_to_hex(dname, cksum.data); md5_to_hex(dname, cksum.data);
kfree(cksum.data);
status = nfs_ok; status = nfs_ok;
out: out:
kfree(cksum.data);
crypto_free_hash(desc.tfm); crypto_free_hash(desc.tfm);
out_no_tfm: out_no_tfm:
return status; return status;
......
...@@ -2416,6 +2416,26 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2416,6 +2416,26 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
#define LOCK_HASH_SIZE (1 << LOCK_HASH_BITS) #define LOCK_HASH_SIZE (1 << LOCK_HASH_BITS)
#define LOCK_HASH_MASK (LOCK_HASH_SIZE - 1) #define LOCK_HASH_MASK (LOCK_HASH_SIZE - 1)
static inline u64
end_offset(u64 start, u64 len)
{
u64 end;
end = start + len;
return end >= start ? end: NFS4_MAX_UINT64;
}
/* last octet in a range */
static inline u64
last_byte_offset(u64 start, u64 len)
{
u64 end;
BUG_ON(!len);
end = start + len;
return end > start ? end - 1: NFS4_MAX_UINT64;
}
#define lockownerid_hashval(id) \ #define lockownerid_hashval(id) \
((id) & LOCK_HASH_MASK) ((id) & LOCK_HASH_MASK)
...@@ -2435,13 +2455,13 @@ static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE]; ...@@ -2435,13 +2455,13 @@ static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE];
static struct nfs4_stateid * static struct nfs4_stateid *
find_stateid(stateid_t *stid, int flags) find_stateid(stateid_t *stid, int flags)
{ {
struct nfs4_stateid *local = NULL; struct nfs4_stateid *local;
u32 st_id = stid->si_stateownerid; u32 st_id = stid->si_stateownerid;
u32 f_id = stid->si_fileid; u32 f_id = stid->si_fileid;
unsigned int hashval; unsigned int hashval;
dprintk("NFSD: find_stateid flags 0x%x\n",flags); dprintk("NFSD: find_stateid flags 0x%x\n",flags);
if ((flags & LOCK_STATE) || (flags & RD_STATE) || (flags & WR_STATE)) { if (flags & (LOCK_STATE | RD_STATE | WR_STATE)) {
hashval = stateid_hashval(st_id, f_id); hashval = stateid_hashval(st_id, f_id);
list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) { list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) {
if ((local->st_stateid.si_stateownerid == st_id) && if ((local->st_stateid.si_stateownerid == st_id) &&
...@@ -2449,7 +2469,8 @@ find_stateid(stateid_t *stid, int flags) ...@@ -2449,7 +2469,8 @@ find_stateid(stateid_t *stid, int flags)
return local; return local;
} }
} }
if ((flags & OPEN_STATE) || (flags & RD_STATE) || (flags & WR_STATE)) {
if (flags & (OPEN_STATE | RD_STATE | WR_STATE)) {
hashval = stateid_hashval(st_id, f_id); hashval = stateid_hashval(st_id, f_id);
list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) { list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) {
if ((local->st_stateid.si_stateownerid == st_id) && if ((local->st_stateid.si_stateownerid == st_id) &&
...@@ -2518,8 +2539,8 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) ...@@ -2518,8 +2539,8 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny)
deny->ld_clientid.cl_id = 0; deny->ld_clientid.cl_id = 0;
} }
deny->ld_start = fl->fl_start; deny->ld_start = fl->fl_start;
deny->ld_length = ~(u64)0; deny->ld_length = NFS4_MAX_UINT64;
if (fl->fl_end != ~(u64)0) if (fl->fl_end != NFS4_MAX_UINT64)
deny->ld_length = fl->fl_end - fl->fl_start + 1; deny->ld_length = fl->fl_end - fl->fl_start + 1;
deny->ld_type = NFS4_READ_LT; deny->ld_type = NFS4_READ_LT;
if (fl->fl_type != F_RDLCK) if (fl->fl_type != F_RDLCK)
...@@ -2616,7 +2637,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc ...@@ -2616,7 +2637,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc
static int static int
check_lock_length(u64 offset, u64 length) check_lock_length(u64 offset, u64 length)
{ {
return ((length == 0) || ((length != ~(u64)0) && return ((length == 0) || ((length != NFS4_MAX_UINT64) &&
LOFF_OVERFLOW(offset, length))); LOFF_OVERFLOW(offset, length)));
} }
...@@ -2736,11 +2757,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2736,11 +2757,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
file_lock.fl_lmops = &nfsd_posix_mng_ops; file_lock.fl_lmops = &nfsd_posix_mng_ops;
file_lock.fl_start = lock->lk_offset; file_lock.fl_start = lock->lk_offset;
if ((lock->lk_length == ~(u64)0) || file_lock.fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
LOFF_OVERFLOW(lock->lk_offset, lock->lk_length))
file_lock.fl_end = ~(u64)0;
else
file_lock.fl_end = lock->lk_offset + lock->lk_length - 1;
nfs4_transform_lock_offset(&file_lock); nfs4_transform_lock_offset(&file_lock);
/* /*
...@@ -2780,6 +2797,25 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2780,6 +2797,25 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status; return status;
} }
/*
* The NFSv4 spec allows a client to do a LOCKT without holding an OPEN,
* so we do a temporary open here just to get an open file to pass to
* vfs_test_lock. (Arguably perhaps test_lock should be done with an
* inode operation.)
*/
static int nfsd_test_lock(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file_lock *lock)
{
struct file *file;
int err;
err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file);
if (err)
return err;
err = vfs_test_lock(file, lock);
nfsd_close(file);
return err;
}
/* /*
* LOCKT operation * LOCKT operation
*/ */
...@@ -2788,7 +2824,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2788,7 +2824,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_lockt *lockt) struct nfsd4_lockt *lockt)
{ {
struct inode *inode; struct inode *inode;
struct file file;
struct file_lock file_lock; struct file_lock file_lock;
int error; int error;
__be32 status; __be32 status;
...@@ -2839,23 +2874,12 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2839,23 +2874,12 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
file_lock.fl_lmops = &nfsd_posix_mng_ops; file_lock.fl_lmops = &nfsd_posix_mng_ops;
file_lock.fl_start = lockt->lt_offset; file_lock.fl_start = lockt->lt_offset;
if ((lockt->lt_length == ~(u64)0) || LOFF_OVERFLOW(lockt->lt_offset, lockt->lt_length)) file_lock.fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length);
file_lock.fl_end = ~(u64)0;
else
file_lock.fl_end = lockt->lt_offset + lockt->lt_length - 1;
nfs4_transform_lock_offset(&file_lock); nfs4_transform_lock_offset(&file_lock);
/* vfs_test_lock uses the struct file _only_ to resolve the inode.
* since LOCKT doesn't require an OPEN, and therefore a struct
* file may not exist, pass vfs_test_lock a struct file with
* only the dentry:inode set.
*/
memset(&file, 0, sizeof (struct file));
file.f_path.dentry = cstate->current_fh.fh_dentry;
status = nfs_ok; status = nfs_ok;
error = vfs_test_lock(&file, &file_lock); error = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock);
if (error) { if (error) {
status = nfserrno(error); status = nfserrno(error);
goto out; goto out;
...@@ -2906,10 +2930,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2906,10 +2930,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
file_lock.fl_lmops = &nfsd_posix_mng_ops; file_lock.fl_lmops = &nfsd_posix_mng_ops;
file_lock.fl_start = locku->lu_offset; file_lock.fl_start = locku->lu_offset;
if ((locku->lu_length == ~(u64)0) || LOFF_OVERFLOW(locku->lu_offset, locku->lu_length)) file_lock.fl_end = last_byte_offset(locku->lu_offset, locku->lu_length);
file_lock.fl_end = ~(u64)0;
else
file_lock.fl_end = locku->lu_offset + locku->lu_length - 1;
nfs4_transform_lock_offset(&file_lock); nfs4_transform_lock_offset(&file_lock);
/* /*
......
/* /*
* fs/nfs/nfs4xdr.c
*
* Server-side XDR for NFSv4 * Server-side XDR for NFSv4
* *
* Copyright (c) 2002 The Regents of the University of Michigan. * Copyright (c) 2002 The Regents of the University of Michigan.
......
...@@ -84,6 +84,8 @@ static ssize_t write_unexport(struct file *file, char *buf, size_t size); ...@@ -84,6 +84,8 @@ static ssize_t write_unexport(struct file *file, char *buf, size_t size);
static ssize_t write_getfd(struct file *file, char *buf, size_t size); static ssize_t write_getfd(struct file *file, char *buf, size_t size);
static ssize_t write_getfs(struct file *file, char *buf, size_t size); static ssize_t write_getfs(struct file *file, char *buf, size_t size);
static ssize_t write_filehandle(struct file *file, char *buf, size_t size); static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size);
static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size);
static ssize_t write_threads(struct file *file, char *buf, size_t size); static ssize_t write_threads(struct file *file, char *buf, size_t size);
static ssize_t write_pool_threads(struct file *file, char *buf, size_t size); static ssize_t write_pool_threads(struct file *file, char *buf, size_t size);
static ssize_t write_versions(struct file *file, char *buf, size_t size); static ssize_t write_versions(struct file *file, char *buf, size_t size);
...@@ -94,9 +96,6 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size); ...@@ -94,9 +96,6 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
#endif #endif
static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size);
static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size);
static ssize_t (*write_op[])(struct file *, char *, size_t) = { static ssize_t (*write_op[])(struct file *, char *, size_t) = {
[NFSD_Svc] = write_svc, [NFSD_Svc] = write_svc,
[NFSD_Add] = write_add, [NFSD_Add] = write_add,
...@@ -106,8 +105,8 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { ...@@ -106,8 +105,8 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = {
[NFSD_Getfd] = write_getfd, [NFSD_Getfd] = write_getfd,
[NFSD_Getfs] = write_getfs, [NFSD_Getfs] = write_getfs,
[NFSD_Fh] = write_filehandle, [NFSD_Fh] = write_filehandle,
[NFSD_FO_UnlockIP] = failover_unlock_ip, [NFSD_FO_UnlockIP] = write_unlock_ip,
[NFSD_FO_UnlockFS] = failover_unlock_fs, [NFSD_FO_UnlockFS] = write_unlock_fs,
[NFSD_Threads] = write_threads, [NFSD_Threads] = write_threads,
[NFSD_Pool_Threads] = write_pool_threads, [NFSD_Pool_Threads] = write_pool_threads,
[NFSD_Versions] = write_versions, [NFSD_Versions] = write_versions,
...@@ -176,10 +175,24 @@ static const struct file_operations exports_operations = { ...@@ -176,10 +175,24 @@ static const struct file_operations exports_operations = {
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
/* /*
* payload - write methods * payload - write methods
* If the method has a response, the response should be put in buf,
* and the length returned. Otherwise return 0 or and -error.
*/ */
/**
* write_svc - Start kernel's NFSD server
*
* Deprecated. /proc/fs/nfsd/threads is preferred.
* Function remains to support old versions of nfs-utils.
*
* Input:
* buf: struct nfsctl_svc
* svc_port: port number of this
* server's listener
* svc_nthreads: number of threads to start
* size: size in bytes of passed in nfsctl_svc
* Output:
* On success: returns zero
* On error: return code is negative errno value
*/
static ssize_t write_svc(struct file *file, char *buf, size_t size) static ssize_t write_svc(struct file *file, char *buf, size_t size)
{ {
struct nfsctl_svc *data; struct nfsctl_svc *data;
...@@ -189,6 +202,30 @@ static ssize_t write_svc(struct file *file, char *buf, size_t size) ...@@ -189,6 +202,30 @@ static ssize_t write_svc(struct file *file, char *buf, size_t size)
return nfsd_svc(data->svc_port, data->svc_nthreads); return nfsd_svc(data->svc_port, data->svc_nthreads);
} }
/**
* write_add - Add or modify client entry in auth unix cache
*
* Deprecated. /proc/net/rpc/auth.unix.ip is preferred.
* Function remains to support old versions of nfs-utils.
*
* Input:
* buf: struct nfsctl_client
* cl_ident: '\0'-terminated C string
* containing domain name
* of client
* cl_naddr: no. of items in cl_addrlist
* cl_addrlist: array of client addresses
* cl_fhkeytype: ignored
* cl_fhkeylen: ignored
* cl_fhkey: ignored
* size: size in bytes of passed in nfsctl_client
* Output:
* On success: returns zero
* On error: return code is negative errno value
*
* Note: Only AF_INET client addresses are passed in, since
* nfsctl_client.cl_addrlist contains only in_addr fields for addresses.
*/
static ssize_t write_add(struct file *file, char *buf, size_t size) static ssize_t write_add(struct file *file, char *buf, size_t size)
{ {
struct nfsctl_client *data; struct nfsctl_client *data;
...@@ -198,6 +235,30 @@ static ssize_t write_add(struct file *file, char *buf, size_t size) ...@@ -198,6 +235,30 @@ static ssize_t write_add(struct file *file, char *buf, size_t size)
return exp_addclient(data); return exp_addclient(data);
} }
/**
* write_del - Remove client from auth unix cache
*
* Deprecated. /proc/net/rpc/auth.unix.ip is preferred.
* Function remains to support old versions of nfs-utils.
*
* Input:
* buf: struct nfsctl_client
* cl_ident: '\0'-terminated C string
* containing domain name
* of client
* cl_naddr: ignored
* cl_addrlist: ignored
* cl_fhkeytype: ignored
* cl_fhkeylen: ignored
* cl_fhkey: ignored
* size: size in bytes of passed in nfsctl_client
* Output:
* On success: returns zero
* On error: return code is negative errno value
*
* Note: Only AF_INET client addresses are passed in, since
* nfsctl_client.cl_addrlist contains only in_addr fields for addresses.
*/
static ssize_t write_del(struct file *file, char *buf, size_t size) static ssize_t write_del(struct file *file, char *buf, size_t size)
{ {
struct nfsctl_client *data; struct nfsctl_client *data;
...@@ -207,6 +268,33 @@ static ssize_t write_del(struct file *file, char *buf, size_t size) ...@@ -207,6 +268,33 @@ static ssize_t write_del(struct file *file, char *buf, size_t size)
return exp_delclient(data); return exp_delclient(data);
} }
/**
* write_export - Export part or all of a local file system
*
* Deprecated. /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred.
* Function remains to support old versions of nfs-utils.
*
* Input:
* buf: struct nfsctl_export
* ex_client: '\0'-terminated C string
* containing domain name
* of client allowed to access
* this export
* ex_path: '\0'-terminated C string
* containing pathname of
* directory in local file system
* ex_dev: fsid to use for this export
* ex_ino: ignored
* ex_flags: export flags for this export
* ex_anon_uid: UID to use for anonymous
* requests
* ex_anon_gid: GID to use for anonymous
* requests
* size: size in bytes of passed in nfsctl_export
* Output:
* On success: returns zero
* On error: return code is negative errno value
*/
static ssize_t write_export(struct file *file, char *buf, size_t size) static ssize_t write_export(struct file *file, char *buf, size_t size)
{ {
struct nfsctl_export *data; struct nfsctl_export *data;
...@@ -216,6 +304,31 @@ static ssize_t write_export(struct file *file, char *buf, size_t size) ...@@ -216,6 +304,31 @@ static ssize_t write_export(struct file *file, char *buf, size_t size)
return exp_export(data); return exp_export(data);
} }
/**
* write_unexport - Unexport a previously exported file system
*
* Deprecated. /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred.
* Function remains to support old versions of nfs-utils.
*
* Input:
* buf: struct nfsctl_export
* ex_client: '\0'-terminated C string
* containing domain name
* of client no longer allowed
* to access this export
* ex_path: '\0'-terminated C string
* containing pathname of
* directory in local file system
* ex_dev: ignored
* ex_ino: ignored
* ex_flags: ignored
* ex_anon_uid: ignored
* ex_anon_gid: ignored
* size: size in bytes of passed in nfsctl_export
* Output:
* On success: returns zero
* On error: return code is negative errno value
*/
static ssize_t write_unexport(struct file *file, char *buf, size_t size) static ssize_t write_unexport(struct file *file, char *buf, size_t size)
{ {
struct nfsctl_export *data; struct nfsctl_export *data;
...@@ -226,6 +339,30 @@ static ssize_t write_unexport(struct file *file, char *buf, size_t size) ...@@ -226,6 +339,30 @@ static ssize_t write_unexport(struct file *file, char *buf, size_t size)
return exp_unexport(data); return exp_unexport(data);
} }
/**
* write_getfs - Get a variable-length NFS file handle by path
*
* Deprecated. /proc/fs/nfsd/filehandle is preferred.
* Function remains to support old versions of nfs-utils.
*
* Input:
* buf: struct nfsctl_fsparm
* gd_addr: socket address of client
* gd_path: '\0'-terminated C string
* containing pathname of
* directory in local file system
* gd_maxlen: maximum size of returned file
* handle
* size: size in bytes of passed in nfsctl_fsparm
* Output:
* On success: passed-in buffer filled with a knfsd_fh structure
* (a variable-length raw NFS file handle);
* return code is the size in bytes of the file handle
* On error: return code is negative errno value
*
* Note: Only AF_INET client addresses are passed in, since gd_addr
* is the same size as a struct sockaddr_in.
*/
static ssize_t write_getfs(struct file *file, char *buf, size_t size) static ssize_t write_getfs(struct file *file, char *buf, size_t size)
{ {
struct nfsctl_fsparm *data; struct nfsctl_fsparm *data;
...@@ -265,6 +402,29 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size) ...@@ -265,6 +402,29 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size)
return err; return err;
} }
/**
* write_getfd - Get a fixed-length NFS file handle by path (used by mountd)
*
* Deprecated. /proc/fs/nfsd/filehandle is preferred.
* Function remains to support old versions of nfs-utils.
*
* Input:
* buf: struct nfsctl_fdparm
* gd_addr: socket address of client
* gd_path: '\0'-terminated C string
* containing pathname of
* directory in local file system
* gd_version: fdparm structure version
* size: size in bytes of passed in nfsctl_fdparm
* Output:
* On success: passed-in buffer filled with nfsctl_res
* (a fixed-length raw NFS file handle);
* return code is the size in bytes of the file handle
* On error: return code is negative errno value
*
* Note: Only AF_INET client addresses are passed in, since gd_addr
* is the same size as a struct sockaddr_in.
*/
static ssize_t write_getfd(struct file *file, char *buf, size_t size) static ssize_t write_getfd(struct file *file, char *buf, size_t size)
{ {
struct nfsctl_fdparm *data; struct nfsctl_fdparm *data;
...@@ -309,7 +469,23 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size) ...@@ -309,7 +469,23 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
return err; return err;
} }
static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) /**
* write_unlock_ip - Release all locks used by a client
*
* Experimental.
*
* Input:
* buf: '\n'-terminated C string containing a
* presentation format IPv4 address
* size: length of C string in @buf
* Output:
* On success: returns zero if all specified locks were released;
* returns one if one or more locks were not released
* On error: return code is negative errno value
*
* Note: Only AF_INET client addresses are passed in
*/
static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
{ {
struct sockaddr_in sin = { struct sockaddr_in sin = {
.sin_family = AF_INET, .sin_family = AF_INET,
...@@ -339,7 +515,21 @@ static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) ...@@ -339,7 +515,21 @@ static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size)
return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin); return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin);
} }
static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size) /**
* write_unlock_fs - Release all locks on a local file system
*
* Experimental.
*
* Input:
* buf: '\n'-terminated C string containing the
* absolute pathname of a local file system
* size: length of C string in @buf
* Output:
* On success: returns zero if all specified locks were released;
* returns one if one or more locks were not released
* On error: return code is negative errno value
*/
static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
{ {
struct path path; struct path path;
char *fo_path; char *fo_path;
...@@ -360,21 +550,44 @@ static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size) ...@@ -360,21 +550,44 @@ static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size)
if (error) if (error)
return error; return error;
/*
* XXX: Needs better sanity checking. Otherwise we could end up
* releasing locks on the wrong file system.
*
* For example:
* 1. Does the path refer to a directory?
* 2. Is that directory a mount point, or
* 3. Is that directory the root of an exported file system?
*/
error = nlmsvc_unlock_all_by_sb(path.mnt->mnt_sb); error = nlmsvc_unlock_all_by_sb(path.mnt->mnt_sb);
path_put(&path); path_put(&path);
return error; return error;
} }
/**
* write_filehandle - Get a variable-length NFS file handle by path
*
* On input, the buffer contains a '\n'-terminated C string comprised of
* three alphanumeric words separated by whitespace. The string may
* contain escape sequences.
*
* Input:
* buf:
* domain: client domain name
* path: export pathname
* maxsize: numeric maximum size of
* @buf
* size: length of C string in @buf
* Output:
* On success: passed-in buffer filled with '\n'-terminated C
* string containing a ASCII hex text version
* of the NFS file handle;
* return code is the size in bytes of the string
* On error: return code is negative errno value
*/
static ssize_t write_filehandle(struct file *file, char *buf, size_t size) static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
{ {
/* request is:
* domain path maxsize
* response is
* filehandle
*
* qword quoting is used, so filehandle will be \x....
*/
char *dname, *path; char *dname, *path;
int uninitialized_var(maxsize); int uninitialized_var(maxsize);
char *mesg = buf; char *mesg = buf;
...@@ -391,11 +604,13 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size) ...@@ -391,11 +604,13 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
dname = mesg; dname = mesg;
len = qword_get(&mesg, dname, size); len = qword_get(&mesg, dname, size);
if (len <= 0) return -EINVAL; if (len <= 0)
return -EINVAL;
path = dname+len+1; path = dname+len+1;
len = qword_get(&mesg, path, size); len = qword_get(&mesg, path, size);
if (len <= 0) return -EINVAL; if (len <= 0)
return -EINVAL;
len = get_int(&mesg, &maxsize); len = get_int(&mesg, &maxsize);
if (len) if (len)
...@@ -419,17 +634,43 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size) ...@@ -419,17 +634,43 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
if (len) if (len)
return len; return len;
mesg = buf; len = SIMPLE_TRANSACTION_LIMIT; mesg = buf;
len = SIMPLE_TRANSACTION_LIMIT;
qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size); qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
mesg[-1] = '\n'; mesg[-1] = '\n';
return mesg - buf; return mesg - buf;
} }
/**
* write_threads - Start NFSD, or report the current number of running threads
*
* Input:
* buf: ignored
* size: zero
* Output:
* On success: passed-in buffer filled with '\n'-terminated C
* string numeric value representing the number of
* running NFSD threads;
* return code is the size in bytes of the string
* On error: return code is zero
*
* OR
*
* Input:
* buf: C string containing an unsigned
* integer value representing the
* number of NFSD threads to start
* size: non-zero length of C string in @buf
* Output:
* On success: NFS service is started;
* passed-in buffer filled with '\n'-terminated C
* string numeric value representing the number of
* running NFSD threads;
* return code is the size in bytes of the string
* On error: return code is zero or a negative errno value
*/
static ssize_t write_threads(struct file *file, char *buf, size_t size) static ssize_t write_threads(struct file *file, char *buf, size_t size)
{ {
/* if size > 0, look for a number of threads and call nfsd_svc
* then write out number of threads as reply
*/
char *mesg = buf; char *mesg = buf;
int rv; int rv;
if (size > 0) { if (size > 0) {
...@@ -437,9 +678,9 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) ...@@ -437,9 +678,9 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
rv = get_int(&mesg, &newthreads); rv = get_int(&mesg, &newthreads);
if (rv) if (rv)
return rv; return rv;
if (newthreads <0) if (newthreads < 0)
return -EINVAL; return -EINVAL;
rv = nfsd_svc(2049, newthreads); rv = nfsd_svc(NFS_PORT, newthreads);
if (rv) if (rv)
return rv; return rv;
} }
...@@ -447,6 +688,28 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) ...@@ -447,6 +688,28 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
return strlen(buf); return strlen(buf);
} }
/**
* write_pool_threads - Set or report the current number of threads per pool
*
* Input:
* buf: ignored
* size: zero
*
* OR
*
* Input:
* buf: C string containing whitespace-
* separated unsigned integer values
* representing the number of NFSD
* threads to start in each pool
* size: non-zero length of C string in @buf
* Output:
* On success: passed-in buffer filled with '\n'-terminated C
* string containing integer values representing the
* number of NFSD threads in each pool;
* return code is the size in bytes of the string
* On error: return code is zero or a negative errno value
*/
static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
{ {
/* if size > 0, look for an array of number of threads per node /* if size > 0, look for an array of number of threads per node
...@@ -517,10 +780,6 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) ...@@ -517,10 +780,6 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
static ssize_t __write_versions(struct file *file, char *buf, size_t size) static ssize_t __write_versions(struct file *file, char *buf, size_t size)
{ {
/*
* Format:
* [-/+]vers [-/+]vers ...
*/
char *mesg = buf; char *mesg = buf;
char *vers, sign; char *vers, sign;
int len, num; int len, num;
...@@ -578,6 +837,38 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) ...@@ -578,6 +837,38 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
return len; return len;
} }
/**
* write_versions - Set or report the available NFS protocol versions
*
* Input:
* buf: ignored
* size: zero
* Output:
* On success: passed-in buffer filled with '\n'-terminated C
* string containing positive or negative integer
* values representing the current status of each
* protocol version;
* return code is the size in bytes of the string
* On error: return code is zero or a negative errno value
*
* OR
*
* Input:
* buf: C string containing whitespace-
* separated positive or negative
* integer values representing NFS
* protocol versions to enable ("+n")
* or disable ("-n")
* size: non-zero length of C string in @buf
* Output:
* On success: status of zero or more protocol versions has
* been updated; passed-in buffer filled with
* '\n'-terminated C string containing positive
* or negative integer values representing the
* current status of each protocol version;
* return code is the size in bytes of the string
* On error: return code is zero or a negative errno value
*/
static ssize_t write_versions(struct file *file, char *buf, size_t size) static ssize_t write_versions(struct file *file, char *buf, size_t size)
{ {
ssize_t rv; ssize_t rv;
...@@ -687,6 +978,75 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size) ...@@ -687,6 +978,75 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size)
return -EINVAL; return -EINVAL;
} }
/**
* write_ports - Pass a socket file descriptor or transport name to listen on
*
* Input:
* buf: ignored
* size: zero
* Output:
* On success: passed-in buffer filled with a '\n'-terminated C
* string containing a whitespace-separated list of
* named NFSD listeners;
* return code is the size in bytes of the string
* On error: return code is zero or a negative errno value
*
* OR
*
* Input:
* buf: C string containing an unsigned
* integer value representing a bound
* but unconnected socket that is to be
* used as an NFSD listener
* size: non-zero length of C string in @buf
* Output:
* On success: NFS service is started;
* passed-in buffer filled with a '\n'-terminated C
* string containing a unique alphanumeric name of
* the listener;
* return code is the size in bytes of the string
* On error: return code is a negative errno value
*
* OR
*
* Input:
* buf: C string containing a "-" followed
* by an integer value representing a
* previously passed in socket file
* descriptor
* size: non-zero length of C string in @buf
* Output:
* On success: NFS service no longer listens on that socket;
* passed-in buffer filled with a '\n'-terminated C
* string containing a unique name of the listener;
* return code is the size in bytes of the string
* On error: return code is a negative errno value
*
* OR
*
* Input:
* buf: C string containing a transport
* name and an unsigned integer value
* representing the port to listen on,
* separated by whitespace
* size: non-zero length of C string in @buf
* Output:
* On success: returns zero; NFS service is started
* On error: return code is a negative errno value
*
* OR
*
* Input:
* buf: C string containing a "-" followed
* by a transport name and an unsigned
* integer value representing the port
* to listen on, separated by whitespace
* size: non-zero length of C string in @buf
* Output:
* On success: returns zero; NFS service no longer listens
* on that transport
* On error: return code is a negative errno value
*/
static ssize_t write_ports(struct file *file, char *buf, size_t size) static ssize_t write_ports(struct file *file, char *buf, size_t size)
{ {
ssize_t rv; ssize_t rv;
...@@ -700,6 +1060,27 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) ...@@ -700,6 +1060,27 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size)
int nfsd_max_blksize; int nfsd_max_blksize;
/**
* write_maxblksize - Set or report the current NFS blksize
*
* Input:
* buf: ignored
* size: zero
*
* OR
*
* Input:
* buf: C string containing an unsigned
* integer value representing the new
* NFS blksize
* size: non-zero length of C string in @buf
* Output:
* On success: passed-in buffer filled with '\n'-terminated C string
* containing numeric value of the current NFS blksize
* setting;
* return code is the size in bytes of the string
* On error: return code is zero or a negative errno value
*/
static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
{ {
char *mesg = buf; char *mesg = buf;
...@@ -752,6 +1133,27 @@ static ssize_t __write_leasetime(struct file *file, char *buf, size_t size) ...@@ -752,6 +1133,27 @@ static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
return strlen(buf); return strlen(buf);
} }
/**
* write_leasetime - Set or report the current NFSv4 lease time
*
* Input:
* buf: ignored
* size: zero
*
* OR
*
* Input:
* buf: C string containing an unsigned
* integer value representing the new
* NFSv4 lease expiry time
* size: non-zero length of C string in @buf
* Output:
* On success: passed-in buffer filled with '\n'-terminated C
* string containing unsigned integer value of the
* current lease expiry time;
* return code is the size in bytes of the string
* On error: return code is zero or a negative errno value
*/
static ssize_t write_leasetime(struct file *file, char *buf, size_t size) static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
{ {
ssize_t rv; ssize_t rv;
...@@ -788,6 +1190,27 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size) ...@@ -788,6 +1190,27 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
return strlen(buf); return strlen(buf);
} }
/**
* write_recoverydir - Set or report the pathname of the recovery directory
*
* Input:
* buf: ignored
* size: zero
*
* OR
*
* Input:
* buf: C string containing the pathname
* of the directory on a local file
* system containing permanent NFSv4
* recovery data
* size: non-zero length of C string in @buf
* Output:
* On success: passed-in buffer filled with '\n'-terminated C string
* containing the current recovery pathname setting;
* return code is the size in bytes of the string
* On error: return code is zero or a negative errno value
*/
static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
{ {
ssize_t rv; ssize_t rv;
......
...@@ -258,14 +258,32 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp) ...@@ -258,14 +258,32 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
return error; return error;
} }
/* /**
* Perform sanity checks on the dentry in a client's file handle. * fh_verify - filehandle lookup and access checking
* @rqstp: pointer to current rpc request
* @fhp: filehandle to be verified
* @type: expected type of object pointed to by filehandle
* @access: type of access needed to object
*
* Look up a dentry from the on-the-wire filehandle, check the client's
* access to the export, and set the current task's credentials.
*
* Regardless of success or failure of fh_verify(), fh_put() should be
* called on @fhp when the caller is finished with the filehandle.
* *
* Note that the file handle dentry may need to be freed even after * fh_verify() may be called multiple times on a given filehandle, for
* an error return. * example, when processing an NFSv4 compound. The first call will look
* up a dentry using the on-the-wire filehandle. Subsequent calls will
* skip the lookup and just perform the other checks and possibly change
* the current task's credentials.
* *
* This is only called at the start of an nfsproc call, so fhp points to * @type specifies the type of object expected using one of the S_IF*
* a svc_fh which is all 0 except for the over-the-wire file handle. * constants defined in include/linux/stat.h. The caller may use zero
* to indicate that it doesn't care, or a negative integer to indicate
* that it expects something not of the given type.
*
* @access is formed from the NFSD_MAY_* constants defined in
* include/linux/nfsd/nfsd.h.
*/ */
__be32 __be32
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
...@@ -466,6 +484,8 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, ...@@ -466,6 +484,8 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
goto retry; goto retry;
break; break;
} }
} else if (exp->ex_flags & NFSEXP_FSID) {
fsid_type = FSID_NUM;
} else if (exp->ex_uuid) { } else if (exp->ex_uuid) {
if (fhp->fh_maxsize >= 64) { if (fhp->fh_maxsize >= 64) {
if (root_export) if (root_export)
...@@ -478,9 +498,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, ...@@ -478,9 +498,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
else else
fsid_type = FSID_UUID4_INUM; fsid_type = FSID_UUID4_INUM;
} }
} else if (exp->ex_flags & NFSEXP_FSID) } else if (!old_valid_dev(ex_dev))
fsid_type = FSID_NUM;
else if (!old_valid_dev(ex_dev))
/* for newer device numbers, we must use a newer fsid format */ /* for newer device numbers, we must use a newer fsid format */
fsid_type = FSID_ENCODE_DEV; fsid_type = FSID_ENCODE_DEV;
else else
......
...@@ -622,6 +622,7 @@ nfserrno (int errno) ...@@ -622,6 +622,7 @@ nfserrno (int errno)
{ nfserr_badname, -ESRCH }, { nfserr_badname, -ESRCH },
{ nfserr_io, -ETXTBSY }, { nfserr_io, -ETXTBSY },
{ nfserr_notsupp, -EOPNOTSUPP }, { nfserr_notsupp, -EOPNOTSUPP },
{ nfserr_toosmall, -ETOOSMALL },
}; };
int i; int i;
......
...@@ -744,16 +744,44 @@ nfsd_close(struct file *filp) ...@@ -744,16 +744,44 @@ nfsd_close(struct file *filp)
fput(filp); fput(filp);
} }
/*
* Sync a file
* As this calls fsync (not fdatasync) there is no need for a write_inode
* after it.
*/
static inline int nfsd_dosync(struct file *filp, struct dentry *dp,
const struct file_operations *fop)
{
struct inode *inode = dp->d_inode;
int (*fsync) (struct file *, struct dentry *, int);
int err;
err = filemap_fdatawrite(inode->i_mapping);
if (err == 0 && fop && (fsync = fop->fsync))
err = fsync(filp, dp, 0);
if (err == 0)
err = filemap_fdatawait(inode->i_mapping);
return err;
}
static int static int
nfsd_sync(struct file *filp) nfsd_sync(struct file *filp)
{ {
return vfs_fsync(filp, filp->f_path.dentry, 0); int err;
struct inode *inode = filp->f_path.dentry->d_inode;
dprintk("nfsd: sync file %s\n", filp->f_path.dentry->d_name.name);
mutex_lock(&inode->i_mutex);
err=nfsd_dosync(filp, filp->f_path.dentry, filp->f_op);
mutex_unlock(&inode->i_mutex);
return err;
} }
int int
nfsd_sync_dir(struct dentry *dentry) nfsd_sync_dir(struct dentry *dp)
{ {
return vfs_fsync(NULL, dentry, 0); return nfsd_dosync(NULL, dp, dp->d_inode->i_fop);
} }
/* /*
......
...@@ -43,8 +43,8 @@ struct nlm_host { ...@@ -43,8 +43,8 @@ struct nlm_host {
struct sockaddr_storage h_addr; /* peer address */ struct sockaddr_storage h_addr; /* peer address */
size_t h_addrlen; size_t h_addrlen;
struct sockaddr_storage h_srcaddr; /* our address (optional) */ struct sockaddr_storage h_srcaddr; /* our address (optional) */
struct rpc_clnt * h_rpcclnt; /* RPC client to talk to peer */ struct rpc_clnt *h_rpcclnt; /* RPC client to talk to peer */
char * h_name; /* remote hostname */ char *h_name; /* remote hostname */
u32 h_version; /* interface version */ u32 h_version; /* interface version */
unsigned short h_proto; /* transport proto */ unsigned short h_proto; /* transport proto */
unsigned short h_reclaiming : 1, unsigned short h_reclaiming : 1,
...@@ -64,21 +64,29 @@ struct nlm_host { ...@@ -64,21 +64,29 @@ struct nlm_host {
spinlock_t h_lock; spinlock_t h_lock;
struct list_head h_granted; /* Locks in GRANTED state */ struct list_head h_granted; /* Locks in GRANTED state */
struct list_head h_reclaim; /* Locks in RECLAIM state */ struct list_head h_reclaim; /* Locks in RECLAIM state */
struct nsm_handle * h_nsmhandle; /* NSM status handle */ struct nsm_handle *h_nsmhandle; /* NSM status handle */
char *h_addrbuf; /* address eyecatcher */
char h_addrbuf[48], /* address eyecatchers */
h_srcaddrbuf[48];
}; };
/*
* The largest string sm_addrbuf should hold is a full-size IPv6 address
* (no "::" anywhere) with a scope ID. The buffer size is computed to
* hold eight groups of colon-separated four-hex-digit numbers, a
* percent sign, a scope id (at most 32 bits, in decimal), and NUL.
*/
#define NSM_ADDRBUF ((8 * 4 + 7) + (1 + 10) + 1)
struct nsm_handle { struct nsm_handle {
struct list_head sm_link; struct list_head sm_link;
atomic_t sm_count; atomic_t sm_count;
char * sm_name; char *sm_mon_name;
char *sm_name;
struct sockaddr_storage sm_addr; struct sockaddr_storage sm_addr;
size_t sm_addrlen; size_t sm_addrlen;
unsigned int sm_monitored : 1, unsigned int sm_monitored : 1,
sm_sticky : 1; /* don't unmonitor */ sm_sticky : 1; /* don't unmonitor */
char sm_addrbuf[48]; /* address eyecatcher */ struct nsm_private sm_priv;
char sm_addrbuf[NSM_ADDRBUF];
}; };
/* /*
...@@ -104,16 +112,6 @@ static inline struct sockaddr *nlm_srcaddr(const struct nlm_host *host) ...@@ -104,16 +112,6 @@ static inline struct sockaddr *nlm_srcaddr(const struct nlm_host *host)
return (struct sockaddr *)&host->h_srcaddr; return (struct sockaddr *)&host->h_srcaddr;
} }
static inline struct sockaddr_in *nsm_addr_in(const struct nsm_handle *handle)
{
return (struct sockaddr_in *)&handle->sm_addr;
}
static inline struct sockaddr *nsm_addr(const struct nsm_handle *handle)
{
return (struct sockaddr *)&handle->sm_addr;
}
/* /*
* Map an fl_owner_t into a unique 32-bit "pid" * Map an fl_owner_t into a unique 32-bit "pid"
*/ */
...@@ -197,6 +195,7 @@ extern struct svc_procedure nlmsvc_procedures4[]; ...@@ -197,6 +195,7 @@ extern struct svc_procedure nlmsvc_procedures4[];
extern int nlmsvc_grace_period; extern int nlmsvc_grace_period;
extern unsigned long nlmsvc_timeout; extern unsigned long nlmsvc_timeout;
extern int nsm_use_hostnames; extern int nsm_use_hostnames;
extern int nsm_local_state;
/* /*
* Lockd client functions * Lockd client functions
...@@ -231,10 +230,20 @@ void nlm_rebind_host(struct nlm_host *); ...@@ -231,10 +230,20 @@ void nlm_rebind_host(struct nlm_host *);
struct nlm_host * nlm_get_host(struct nlm_host *); struct nlm_host * nlm_get_host(struct nlm_host *);
void nlm_release_host(struct nlm_host *); void nlm_release_host(struct nlm_host *);
void nlm_shutdown_hosts(void); void nlm_shutdown_hosts(void);
extern void nlm_host_rebooted(const struct sockaddr_in *, const char *, void nlm_host_rebooted(const struct nlm_reboot *);
unsigned int, u32);
void nsm_release(struct nsm_handle *);
/*
* Host monitoring
*/
int nsm_monitor(const struct nlm_host *host);
void nsm_unmonitor(const struct nlm_host *host);
struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,
const size_t salen,
const char *hostname,
const size_t hostname_len);
struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info);
void nsm_release(struct nsm_handle *nsm);
/* /*
* This is used in garbage collection and resource reclaim * This is used in garbage collection and resource reclaim
...@@ -282,16 +291,25 @@ static inline struct inode *nlmsvc_file_inode(struct nlm_file *file) ...@@ -282,16 +291,25 @@ static inline struct inode *nlmsvc_file_inode(struct nlm_file *file)
static inline int __nlm_privileged_request4(const struct sockaddr *sap) static inline int __nlm_privileged_request4(const struct sockaddr *sap)
{ {
const struct sockaddr_in *sin = (struct sockaddr_in *)sap; const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
return (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) &&
(ntohs(sin->sin_port) < 1024); if (ntohs(sin->sin_port) > 1023)
return 0;
return ipv4_is_loopback(sin->sin_addr.s_addr);
} }
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static inline int __nlm_privileged_request6(const struct sockaddr *sap) static inline int __nlm_privileged_request6(const struct sockaddr *sap)
{ {
const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
return (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LOOPBACK) &&
(ntohs(sin6->sin6_port) < 1024); if (ntohs(sin6->sin6_port) > 1023)
return 0;
if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_MAPPED)
return ipv4_is_loopback(sin6->sin6_addr.s6_addr32[3]);
return ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LOOPBACK;
} }
#else /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ #else /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
static inline int __nlm_privileged_request6(const struct sockaddr *sap) static inline int __nlm_privileged_request6(const struct sockaddr *sap)
......
/*
* linux/include/linux/lockd/sm_inter.h
*
* Declarations for the kernel statd client.
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#ifndef LINUX_LOCKD_SM_INTER_H
#define LINUX_LOCKD_SM_INTER_H
#define SM_PROGRAM 100024
#define SM_VERSION 1
#define SM_STAT 1
#define SM_MON 2
#define SM_UNMON 3
#define SM_UNMON_ALL 4
#define SM_SIMU_CRASH 5
#define SM_NOTIFY 6
#define SM_MAXSTRLEN 1024
#define SM_PRIV_SIZE 16
/*
* Arguments for all calls to statd
*/
struct nsm_args {
__be32 addr; /* remote address */
u32 prog; /* RPC callback info */
u32 vers;
u32 proc;
char * mon_name;
};
/*
* Result returned by statd
*/
struct nsm_res {
u32 status;
u32 state;
};
int nsm_monitor(struct nlm_host *);
int nsm_unmonitor(struct nlm_host *);
extern int nsm_local_state;
#endif /* LINUX_LOCKD_SM_INTER_H */
...@@ -13,6 +13,13 @@ ...@@ -13,6 +13,13 @@
#include <linux/nfs.h> #include <linux/nfs.h>
#include <linux/sunrpc/xdr.h> #include <linux/sunrpc/xdr.h>
#define SM_MAXSTRLEN 1024
#define SM_PRIV_SIZE 16
struct nsm_private {
unsigned char data[SM_PRIV_SIZE];
};
struct svc_rqst; struct svc_rqst;
#define NLM_MAXCOOKIELEN 32 #define NLM_MAXCOOKIELEN 32
...@@ -77,10 +84,10 @@ struct nlm_res { ...@@ -77,10 +84,10 @@ struct nlm_res {
* statd callback when client has rebooted * statd callback when client has rebooted
*/ */
struct nlm_reboot { struct nlm_reboot {
char * mon; char *mon;
unsigned int len; unsigned int len;
u32 state; u32 state;
__be32 addr; struct nsm_private priv;
}; };
/* /*
......
...@@ -88,6 +88,8 @@ ...@@ -88,6 +88,8 @@
#define NFS4_ACE_GENERIC_EXECUTE 0x001200A0 #define NFS4_ACE_GENERIC_EXECUTE 0x001200A0
#define NFS4_ACE_MASK_ALL 0x001F01FF #define NFS4_ACE_MASK_ALL 0x001F01FF
#define NFS4_MAX_UINT64 (~(u64)0)
enum nfs4_acl_whotype { enum nfs4_acl_whotype {
NFS4_ACL_WHO_NAMED = 0, NFS4_ACL_WHO_NAMED = 0,
NFS4_ACL_WHO_OWNER, NFS4_ACL_WHO_OWNER,
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
/* /*
* nfsd version * nfsd version
*/ */
#define NFSD_VERSION "0.5"
#define NFSD_SUPPORTED_MINOR_VERSION 0 #define NFSD_SUPPORTED_MINOR_VERSION 0
/* /*
......
...@@ -68,6 +68,10 @@ struct nfs_fhbase_old { ...@@ -68,6 +68,10 @@ struct nfs_fhbase_old {
* 1 - 4 byte user specified identifier * 1 - 4 byte user specified identifier
* 2 - 4 byte major, 4 byte minor, 4 byte inode number - DEPRECATED * 2 - 4 byte major, 4 byte minor, 4 byte inode number - DEPRECATED
* 3 - 4 byte device id, encoded for user-space, 4 byte inode number * 3 - 4 byte device id, encoded for user-space, 4 byte inode number
* 4 - 4 byte inode number and 4 byte uuid
* 5 - 8 byte uuid
* 6 - 16 byte uuid
* 7 - 8 byte inode number and 16 byte uuid
* *
* The fileid_type identified how the file within the filesystem is encoded. * The fileid_type identified how the file within the filesystem is encoded.
* This is (will be) passed to, and set by, the underlying filesystem if it supports * This is (will be) passed to, and set by, the underlying filesystem if it supports
......
...@@ -58,10 +58,13 @@ struct svc_serv { ...@@ -58,10 +58,13 @@ struct svc_serv {
struct svc_stat * sv_stats; /* RPC statistics */ struct svc_stat * sv_stats; /* RPC statistics */
spinlock_t sv_lock; spinlock_t sv_lock;
unsigned int sv_nrthreads; /* # of server threads */ unsigned int sv_nrthreads; /* # of server threads */
unsigned int sv_maxconn; /* max connections allowed or
* '0' causing max to be based
* on number of threads. */
unsigned int sv_max_payload; /* datagram payload size */ unsigned int sv_max_payload; /* datagram payload size */
unsigned int sv_max_mesg; /* max_payload + 1 page for overheads */ unsigned int sv_max_mesg; /* max_payload + 1 page for overheads */
unsigned int sv_xdrsize; /* XDR buffer size */ unsigned int sv_xdrsize; /* XDR buffer size */
struct list_head sv_permsocks; /* all permanent sockets */ struct list_head sv_permsocks; /* all permanent sockets */
struct list_head sv_tempsocks; /* all temporary sockets */ struct list_head sv_tempsocks; /* all temporary sockets */
int sv_tmpcnt; /* count of temporary sockets */ int sv_tmpcnt; /* count of temporary sockets */
......
...@@ -98,7 +98,7 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, ...@@ -98,7 +98,7 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
return new; return new;
} }
EXPORT_SYMBOL(sunrpc_cache_lookup); EXPORT_SYMBOL_GPL(sunrpc_cache_lookup);
static void queue_loose(struct cache_detail *detail, struct cache_head *ch); static void queue_loose(struct cache_detail *detail, struct cache_head *ch);
...@@ -173,7 +173,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail, ...@@ -173,7 +173,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
cache_put(old, detail); cache_put(old, detail);
return tmp; return tmp;
} }
EXPORT_SYMBOL(sunrpc_cache_update); EXPORT_SYMBOL_GPL(sunrpc_cache_update);
static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h); static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h);
/* /*
...@@ -245,7 +245,7 @@ int cache_check(struct cache_detail *detail, ...@@ -245,7 +245,7 @@ int cache_check(struct cache_detail *detail,
cache_put(h, detail); cache_put(h, detail);
return rv; return rv;
} }
EXPORT_SYMBOL(cache_check); EXPORT_SYMBOL_GPL(cache_check);
/* /*
* caches need to be periodically cleaned. * caches need to be periodically cleaned.
...@@ -373,7 +373,7 @@ int cache_register(struct cache_detail *cd) ...@@ -373,7 +373,7 @@ int cache_register(struct cache_detail *cd)
schedule_delayed_work(&cache_cleaner, 0); schedule_delayed_work(&cache_cleaner, 0);
return 0; return 0;
} }
EXPORT_SYMBOL(cache_register); EXPORT_SYMBOL_GPL(cache_register);
void cache_unregister(struct cache_detail *cd) void cache_unregister(struct cache_detail *cd)
{ {
...@@ -399,7 +399,7 @@ void cache_unregister(struct cache_detail *cd) ...@@ -399,7 +399,7 @@ void cache_unregister(struct cache_detail *cd)
out: out:
printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name); printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name);
} }
EXPORT_SYMBOL(cache_unregister); EXPORT_SYMBOL_GPL(cache_unregister);
/* clean cache tries to find something to clean /* clean cache tries to find something to clean
* and cleans it. * and cleans it.
...@@ -514,7 +514,7 @@ void cache_flush(void) ...@@ -514,7 +514,7 @@ void cache_flush(void)
while (cache_clean() != -1) while (cache_clean() != -1)
cond_resched(); cond_resched();
} }
EXPORT_SYMBOL(cache_flush); EXPORT_SYMBOL_GPL(cache_flush);
void cache_purge(struct cache_detail *detail) void cache_purge(struct cache_detail *detail)
{ {
...@@ -523,7 +523,7 @@ void cache_purge(struct cache_detail *detail) ...@@ -523,7 +523,7 @@ void cache_purge(struct cache_detail *detail)
cache_flush(); cache_flush();
detail->flush_time = 1; detail->flush_time = 1;
} }
EXPORT_SYMBOL(cache_purge); EXPORT_SYMBOL_GPL(cache_purge);
/* /*
...@@ -988,7 +988,7 @@ void qword_add(char **bpp, int *lp, char *str) ...@@ -988,7 +988,7 @@ void qword_add(char **bpp, int *lp, char *str)
*bpp = bp; *bpp = bp;
*lp = len; *lp = len;
} }
EXPORT_SYMBOL(qword_add); EXPORT_SYMBOL_GPL(qword_add);
void qword_addhex(char **bpp, int *lp, char *buf, int blen) void qword_addhex(char **bpp, int *lp, char *buf, int blen)
{ {
...@@ -1017,7 +1017,7 @@ void qword_addhex(char **bpp, int *lp, char *buf, int blen) ...@@ -1017,7 +1017,7 @@ void qword_addhex(char **bpp, int *lp, char *buf, int blen)
*bpp = bp; *bpp = bp;
*lp = len; *lp = len;
} }
EXPORT_SYMBOL(qword_addhex); EXPORT_SYMBOL_GPL(qword_addhex);
static void warn_no_listener(struct cache_detail *detail) static void warn_no_listener(struct cache_detail *detail)
{ {
...@@ -1140,7 +1140,7 @@ int qword_get(char **bpp, char *dest, int bufsize) ...@@ -1140,7 +1140,7 @@ int qword_get(char **bpp, char *dest, int bufsize)
*dest = '\0'; *dest = '\0';
return len; return len;
} }
EXPORT_SYMBOL(qword_get); EXPORT_SYMBOL_GPL(qword_get);
/* /*
......
...@@ -106,7 +106,7 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp) { ...@@ -106,7 +106,7 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp) {
seq_putc(seq, '\n'); seq_putc(seq, '\n');
} }
} }
EXPORT_SYMBOL(svc_seq_show); EXPORT_SYMBOL_GPL(svc_seq_show);
/** /**
* rpc_alloc_iostats - allocate an rpc_iostats structure * rpc_alloc_iostats - allocate an rpc_iostats structure
...@@ -249,14 +249,14 @@ svc_proc_register(struct svc_stat *statp, const struct file_operations *fops) ...@@ -249,14 +249,14 @@ svc_proc_register(struct svc_stat *statp, const struct file_operations *fops)
{ {
return do_register(statp->program->pg_name, statp, fops); return do_register(statp->program->pg_name, statp, fops);
} }
EXPORT_SYMBOL(svc_proc_register); EXPORT_SYMBOL_GPL(svc_proc_register);
void void
svc_proc_unregister(const char *name) svc_proc_unregister(const char *name)
{ {
remove_proc_entry(name, proc_net_rpc); remove_proc_entry(name, proc_net_rpc);
} }
EXPORT_SYMBOL(svc_proc_unregister); EXPORT_SYMBOL_GPL(svc_proc_unregister);
void void
rpc_proc_init(void) rpc_proc_init(void)
......
...@@ -431,7 +431,7 @@ svc_create(struct svc_program *prog, unsigned int bufsize, ...@@ -431,7 +431,7 @@ svc_create(struct svc_program *prog, unsigned int bufsize,
{ {
return __svc_create(prog, bufsize, /*npools*/1, family, shutdown); return __svc_create(prog, bufsize, /*npools*/1, family, shutdown);
} }
EXPORT_SYMBOL(svc_create); EXPORT_SYMBOL_GPL(svc_create);
struct svc_serv * struct svc_serv *
svc_create_pooled(struct svc_program *prog, unsigned int bufsize, svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
...@@ -450,7 +450,7 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize, ...@@ -450,7 +450,7 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
return serv; return serv;
} }
EXPORT_SYMBOL(svc_create_pooled); EXPORT_SYMBOL_GPL(svc_create_pooled);
/* /*
* Destroy an RPC service. Should be called with appropriate locking to * Destroy an RPC service. Should be called with appropriate locking to
...@@ -492,7 +492,7 @@ svc_destroy(struct svc_serv *serv) ...@@ -492,7 +492,7 @@ svc_destroy(struct svc_serv *serv)
kfree(serv->sv_pools); kfree(serv->sv_pools);
kfree(serv); kfree(serv);
} }
EXPORT_SYMBOL(svc_destroy); EXPORT_SYMBOL_GPL(svc_destroy);
/* /*
* Allocate an RPC server's buffer space. * Allocate an RPC server's buffer space.
...@@ -567,7 +567,7 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool) ...@@ -567,7 +567,7 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool)
out_enomem: out_enomem:
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
EXPORT_SYMBOL(svc_prepare_thread); EXPORT_SYMBOL_GPL(svc_prepare_thread);
/* /*
* Choose a pool in which to create a new thread, for svc_set_num_threads * Choose a pool in which to create a new thread, for svc_set_num_threads
...@@ -689,7 +689,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) ...@@ -689,7 +689,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
return error; return error;
} }
EXPORT_SYMBOL(svc_set_num_threads); EXPORT_SYMBOL_GPL(svc_set_num_threads);
/* /*
* Called from a server thread as it's exiting. Caller must hold the BKL or * Called from a server thread as it's exiting. Caller must hold the BKL or
...@@ -717,7 +717,7 @@ svc_exit_thread(struct svc_rqst *rqstp) ...@@ -717,7 +717,7 @@ svc_exit_thread(struct svc_rqst *rqstp)
if (serv) if (serv)
svc_destroy(serv); svc_destroy(serv);
} }
EXPORT_SYMBOL(svc_exit_thread); EXPORT_SYMBOL_GPL(svc_exit_thread);
#ifdef CONFIG_SUNRPC_REGISTER_V4 #ifdef CONFIG_SUNRPC_REGISTER_V4
...@@ -1231,7 +1231,7 @@ svc_process(struct svc_rqst *rqstp) ...@@ -1231,7 +1231,7 @@ svc_process(struct svc_rqst *rqstp)
svc_putnl(resv, ntohl(rpc_stat)); svc_putnl(resv, ntohl(rpc_stat));
goto sendit; goto sendit;
} }
EXPORT_SYMBOL(svc_process); EXPORT_SYMBOL_GPL(svc_process);
/* /*
* Return (transport-specific) limit on the rpc payload. * Return (transport-specific) limit on the rpc payload.
......
...@@ -440,7 +440,7 @@ void svc_reserve(struct svc_rqst *rqstp, int space) ...@@ -440,7 +440,7 @@ void svc_reserve(struct svc_rqst *rqstp, int space)
svc_xprt_enqueue(xprt); svc_xprt_enqueue(xprt);
} }
} }
EXPORT_SYMBOL(svc_reserve); EXPORT_SYMBOL_GPL(svc_reserve);
static void svc_xprt_release(struct svc_rqst *rqstp) static void svc_xprt_release(struct svc_rqst *rqstp)
{ {
...@@ -448,6 +448,9 @@ static void svc_xprt_release(struct svc_rqst *rqstp) ...@@ -448,6 +448,9 @@ static void svc_xprt_release(struct svc_rqst *rqstp)
rqstp->rq_xprt->xpt_ops->xpo_release_rqst(rqstp); rqstp->rq_xprt->xpt_ops->xpo_release_rqst(rqstp);
kfree(rqstp->rq_deferred);
rqstp->rq_deferred = NULL;
svc_free_res_pages(rqstp); svc_free_res_pages(rqstp);
rqstp->rq_res.page_len = 0; rqstp->rq_res.page_len = 0;
rqstp->rq_res.page_base = 0; rqstp->rq_res.page_base = 0;
...@@ -498,7 +501,7 @@ void svc_wake_up(struct svc_serv *serv) ...@@ -498,7 +501,7 @@ void svc_wake_up(struct svc_serv *serv)
spin_unlock_bh(&pool->sp_lock); spin_unlock_bh(&pool->sp_lock);
} }
} }
EXPORT_SYMBOL(svc_wake_up); EXPORT_SYMBOL_GPL(svc_wake_up);
int svc_port_is_privileged(struct sockaddr *sin) int svc_port_is_privileged(struct sockaddr *sin)
{ {
...@@ -515,8 +518,10 @@ int svc_port_is_privileged(struct sockaddr *sin) ...@@ -515,8 +518,10 @@ int svc_port_is_privileged(struct sockaddr *sin)
} }
/* /*
* Make sure that we don't have too many active connections. If we * Make sure that we don't have too many active connections. If we have,
* have, something must be dropped. * something must be dropped. It's not clear what will happen if we allow
* "too many" connections, but when dealing with network-facing software,
* we have to code defensively. Here we do that by imposing hard limits.
* *
* There's no point in trying to do random drop here for DoS * There's no point in trying to do random drop here for DoS
* prevention. The NFS clients does 1 reconnect in 15 seconds. An * prevention. The NFS clients does 1 reconnect in 15 seconds. An
...@@ -525,19 +530,27 @@ int svc_port_is_privileged(struct sockaddr *sin) ...@@ -525,19 +530,27 @@ int svc_port_is_privileged(struct sockaddr *sin)
* The only somewhat efficient mechanism would be if drop old * The only somewhat efficient mechanism would be if drop old
* connections from the same IP first. But right now we don't even * connections from the same IP first. But right now we don't even
* record the client IP in svc_sock. * record the client IP in svc_sock.
*
* single-threaded services that expect a lot of clients will probably
* need to set sv_maxconn to override the default value which is based
* on the number of threads
*/ */
static void svc_check_conn_limits(struct svc_serv *serv) static void svc_check_conn_limits(struct svc_serv *serv)
{ {
if (serv->sv_tmpcnt > (serv->sv_nrthreads+3)*20) { unsigned int limit = serv->sv_maxconn ? serv->sv_maxconn :
(serv->sv_nrthreads+3) * 20;
if (serv->sv_tmpcnt > limit) {
struct svc_xprt *xprt = NULL; struct svc_xprt *xprt = NULL;
spin_lock_bh(&serv->sv_lock); spin_lock_bh(&serv->sv_lock);
if (!list_empty(&serv->sv_tempsocks)) { if (!list_empty(&serv->sv_tempsocks)) {
if (net_ratelimit()) { if (net_ratelimit()) {
/* Try to help the admin */ /* Try to help the admin */
printk(KERN_NOTICE "%s: too many open " printk(KERN_NOTICE "%s: too many open "
"connections, consider increasing the " "connections, consider increasing %s\n",
"number of nfsd threads\n", serv->sv_name, serv->sv_maxconn ?
serv->sv_name); "the max number of connections." :
"the number of threads.");
} }
/* /*
* Always select the oldest connection. It's not fair, * Always select the oldest connection. It's not fair,
...@@ -730,7 +743,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout) ...@@ -730,7 +743,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
serv->sv_stats->netcnt++; serv->sv_stats->netcnt++;
return len; return len;
} }
EXPORT_SYMBOL(svc_recv); EXPORT_SYMBOL_GPL(svc_recv);
/* /*
* Drop request * Drop request
...@@ -740,7 +753,7 @@ void svc_drop(struct svc_rqst *rqstp) ...@@ -740,7 +753,7 @@ void svc_drop(struct svc_rqst *rqstp)
dprintk("svc: xprt %p dropped request\n", rqstp->rq_xprt); dprintk("svc: xprt %p dropped request\n", rqstp->rq_xprt);
svc_xprt_release(rqstp); svc_xprt_release(rqstp);
} }
EXPORT_SYMBOL(svc_drop); EXPORT_SYMBOL_GPL(svc_drop);
/* /*
* Return reply to client. * Return reply to client.
...@@ -837,6 +850,11 @@ static void svc_age_temp_xprts(unsigned long closure) ...@@ -837,6 +850,11 @@ static void svc_age_temp_xprts(unsigned long closure)
void svc_delete_xprt(struct svc_xprt *xprt) void svc_delete_xprt(struct svc_xprt *xprt)
{ {
struct svc_serv *serv = xprt->xpt_server; struct svc_serv *serv = xprt->xpt_server;
struct svc_deferred_req *dr;
/* Only do this once */
if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags))
return;
dprintk("svc: svc_delete_xprt(%p)\n", xprt); dprintk("svc: svc_delete_xprt(%p)\n", xprt);
xprt->xpt_ops->xpo_detach(xprt); xprt->xpt_ops->xpo_detach(xprt);
...@@ -851,12 +869,16 @@ void svc_delete_xprt(struct svc_xprt *xprt) ...@@ -851,12 +869,16 @@ void svc_delete_xprt(struct svc_xprt *xprt)
* while still attached to a queue, the queue itself * while still attached to a queue, the queue itself
* is about to be destroyed (in svc_destroy). * is about to be destroyed (in svc_destroy).
*/ */
if (!test_and_set_bit(XPT_DEAD, &xprt->xpt_flags)) { if (test_bit(XPT_TEMP, &xprt->xpt_flags))
BUG_ON(atomic_read(&xprt->xpt_ref.refcount) < 2); serv->sv_tmpcnt--;
if (test_bit(XPT_TEMP, &xprt->xpt_flags))
serv->sv_tmpcnt--; for (dr = svc_deferred_dequeue(xprt); dr;
dr = svc_deferred_dequeue(xprt)) {
svc_xprt_put(xprt); svc_xprt_put(xprt);
kfree(dr);
} }
svc_xprt_put(xprt);
spin_unlock_bh(&serv->sv_lock); spin_unlock_bh(&serv->sv_lock);
} }
...@@ -902,17 +924,19 @@ static void svc_revisit(struct cache_deferred_req *dreq, int too_many) ...@@ -902,17 +924,19 @@ static void svc_revisit(struct cache_deferred_req *dreq, int too_many)
container_of(dreq, struct svc_deferred_req, handle); container_of(dreq, struct svc_deferred_req, handle);
struct svc_xprt *xprt = dr->xprt; struct svc_xprt *xprt = dr->xprt;
if (too_many) { spin_lock(&xprt->xpt_lock);
set_bit(XPT_DEFERRED, &xprt->xpt_flags);
if (too_many || test_bit(XPT_DEAD, &xprt->xpt_flags)) {
spin_unlock(&xprt->xpt_lock);
dprintk("revisit canceled\n");
svc_xprt_put(xprt); svc_xprt_put(xprt);
kfree(dr); kfree(dr);
return; return;
} }
dprintk("revisit queued\n"); dprintk("revisit queued\n");
dr->xprt = NULL; dr->xprt = NULL;
spin_lock(&xprt->xpt_lock);
list_add(&dr->handle.recent, &xprt->xpt_deferred); list_add(&dr->handle.recent, &xprt->xpt_deferred);
spin_unlock(&xprt->xpt_lock); spin_unlock(&xprt->xpt_lock);
set_bit(XPT_DEFERRED, &xprt->xpt_flags);
svc_xprt_enqueue(xprt); svc_xprt_enqueue(xprt);
svc_xprt_put(xprt); svc_xprt_put(xprt);
} }
......
...@@ -57,13 +57,13 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp) ...@@ -57,13 +57,13 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp)
rqstp->rq_authop = aops; rqstp->rq_authop = aops;
return aops->accept(rqstp, authp); return aops->accept(rqstp, authp);
} }
EXPORT_SYMBOL(svc_authenticate); EXPORT_SYMBOL_GPL(svc_authenticate);
int svc_set_client(struct svc_rqst *rqstp) int svc_set_client(struct svc_rqst *rqstp)
{ {
return rqstp->rq_authop->set_client(rqstp); return rqstp->rq_authop->set_client(rqstp);
} }
EXPORT_SYMBOL(svc_set_client); EXPORT_SYMBOL_GPL(svc_set_client);
/* A request, which was authenticated, has now executed. /* A request, which was authenticated, has now executed.
* Time to finalise the credentials and verifier * Time to finalise the credentials and verifier
...@@ -95,7 +95,7 @@ svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops) ...@@ -95,7 +95,7 @@ svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops)
spin_unlock(&authtab_lock); spin_unlock(&authtab_lock);
return rv; return rv;
} }
EXPORT_SYMBOL(svc_auth_register); EXPORT_SYMBOL_GPL(svc_auth_register);
void void
svc_auth_unregister(rpc_authflavor_t flavor) svc_auth_unregister(rpc_authflavor_t flavor)
...@@ -105,7 +105,7 @@ svc_auth_unregister(rpc_authflavor_t flavor) ...@@ -105,7 +105,7 @@ svc_auth_unregister(rpc_authflavor_t flavor)
authtab[flavor] = NULL; authtab[flavor] = NULL;
spin_unlock(&authtab_lock); spin_unlock(&authtab_lock);
} }
EXPORT_SYMBOL(svc_auth_unregister); EXPORT_SYMBOL_GPL(svc_auth_unregister);
/************************************************** /**************************************************
* 'auth_domains' are stored in a hash table indexed by name. * 'auth_domains' are stored in a hash table indexed by name.
...@@ -132,7 +132,7 @@ void auth_domain_put(struct auth_domain *dom) ...@@ -132,7 +132,7 @@ void auth_domain_put(struct auth_domain *dom)
spin_unlock(&auth_domain_lock); spin_unlock(&auth_domain_lock);
} }
} }
EXPORT_SYMBOL(auth_domain_put); EXPORT_SYMBOL_GPL(auth_domain_put);
struct auth_domain * struct auth_domain *
auth_domain_lookup(char *name, struct auth_domain *new) auth_domain_lookup(char *name, struct auth_domain *new)
...@@ -157,10 +157,10 @@ auth_domain_lookup(char *name, struct auth_domain *new) ...@@ -157,10 +157,10 @@ auth_domain_lookup(char *name, struct auth_domain *new)
spin_unlock(&auth_domain_lock); spin_unlock(&auth_domain_lock);
return new; return new;
} }
EXPORT_SYMBOL(auth_domain_lookup); EXPORT_SYMBOL_GPL(auth_domain_lookup);
struct auth_domain *auth_domain_find(char *name) struct auth_domain *auth_domain_find(char *name)
{ {
return auth_domain_lookup(name, NULL); return auth_domain_lookup(name, NULL);
} }
EXPORT_SYMBOL(auth_domain_find); EXPORT_SYMBOL_GPL(auth_domain_find);
...@@ -64,7 +64,7 @@ struct auth_domain *unix_domain_find(char *name) ...@@ -64,7 +64,7 @@ struct auth_domain *unix_domain_find(char *name)
rv = auth_domain_lookup(name, &new->h); rv = auth_domain_lookup(name, &new->h);
} }
} }
EXPORT_SYMBOL(unix_domain_find); EXPORT_SYMBOL_GPL(unix_domain_find);
static void svcauth_unix_domain_release(struct auth_domain *dom) static void svcauth_unix_domain_release(struct auth_domain *dom)
{ {
...@@ -358,7 +358,7 @@ int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom) ...@@ -358,7 +358,7 @@ int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom)
else else
return -ENOMEM; return -ENOMEM;
} }
EXPORT_SYMBOL(auth_unix_add_addr); EXPORT_SYMBOL_GPL(auth_unix_add_addr);
int auth_unix_forget_old(struct auth_domain *dom) int auth_unix_forget_old(struct auth_domain *dom)
{ {
...@@ -370,7 +370,7 @@ int auth_unix_forget_old(struct auth_domain *dom) ...@@ -370,7 +370,7 @@ int auth_unix_forget_old(struct auth_domain *dom)
udom->addr_changes++; udom->addr_changes++;
return 0; return 0;
} }
EXPORT_SYMBOL(auth_unix_forget_old); EXPORT_SYMBOL_GPL(auth_unix_forget_old);
struct auth_domain *auth_unix_lookup(struct in6_addr *addr) struct auth_domain *auth_unix_lookup(struct in6_addr *addr)
{ {
...@@ -395,13 +395,13 @@ struct auth_domain *auth_unix_lookup(struct in6_addr *addr) ...@@ -395,13 +395,13 @@ struct auth_domain *auth_unix_lookup(struct in6_addr *addr)
cache_put(&ipm->h, &ip_map_cache); cache_put(&ipm->h, &ip_map_cache);
return rv; return rv;
} }
EXPORT_SYMBOL(auth_unix_lookup); EXPORT_SYMBOL_GPL(auth_unix_lookup);
void svcauth_unix_purge(void) void svcauth_unix_purge(void)
{ {
cache_purge(&ip_map_cache); cache_purge(&ip_map_cache);
} }
EXPORT_SYMBOL(svcauth_unix_purge); EXPORT_SYMBOL_GPL(svcauth_unix_purge);
static inline struct ip_map * static inline struct ip_map *
ip_map_cached_get(struct svc_rqst *rqstp) ip_map_cached_get(struct svc_rqst *rqstp)
...@@ -714,7 +714,7 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) ...@@ -714,7 +714,7 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
return SVC_OK; return SVC_OK;
} }
EXPORT_SYMBOL(svcauth_unix_set_client); EXPORT_SYMBOL_GPL(svcauth_unix_set_client);
static int static int
svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp) svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
......
...@@ -59,6 +59,7 @@ static void svc_udp_data_ready(struct sock *, int); ...@@ -59,6 +59,7 @@ static void svc_udp_data_ready(struct sock *, int);
static int svc_udp_recvfrom(struct svc_rqst *); static int svc_udp_recvfrom(struct svc_rqst *);
static int svc_udp_sendto(struct svc_rqst *); static int svc_udp_sendto(struct svc_rqst *);
static void svc_sock_detach(struct svc_xprt *); static void svc_sock_detach(struct svc_xprt *);
static void svc_tcp_sock_detach(struct svc_xprt *);
static void svc_sock_free(struct svc_xprt *); static void svc_sock_free(struct svc_xprt *);
static struct svc_xprt *svc_create_socket(struct svc_serv *, int, static struct svc_xprt *svc_create_socket(struct svc_serv *, int,
...@@ -102,7 +103,6 @@ static void svc_reclassify_socket(struct socket *sock) ...@@ -102,7 +103,6 @@ static void svc_reclassify_socket(struct socket *sock)
static void svc_release_skb(struct svc_rqst *rqstp) static void svc_release_skb(struct svc_rqst *rqstp)
{ {
struct sk_buff *skb = rqstp->rq_xprt_ctxt; struct sk_buff *skb = rqstp->rq_xprt_ctxt;
struct svc_deferred_req *dr = rqstp->rq_deferred;
if (skb) { if (skb) {
struct svc_sock *svsk = struct svc_sock *svsk =
...@@ -112,10 +112,6 @@ static void svc_release_skb(struct svc_rqst *rqstp) ...@@ -112,10 +112,6 @@ static void svc_release_skb(struct svc_rqst *rqstp)
dprintk("svc: service %p, releasing skb %p\n", rqstp, skb); dprintk("svc: service %p, releasing skb %p\n", rqstp, skb);
skb_free_datagram(svsk->sk_sk, skb); skb_free_datagram(svsk->sk_sk, skb);
} }
if (dr) {
rqstp->rq_deferred = NULL;
kfree(dr);
}
} }
union svc_pktinfo_u { union svc_pktinfo_u {
...@@ -289,7 +285,7 @@ svc_sock_names(char *buf, struct svc_serv *serv, char *toclose) ...@@ -289,7 +285,7 @@ svc_sock_names(char *buf, struct svc_serv *serv, char *toclose)
return -ENOENT; return -ENOENT;
return len; return len;
} }
EXPORT_SYMBOL(svc_sock_names); EXPORT_SYMBOL_GPL(svc_sock_names);
/* /*
* Check input queue length * Check input queue length
...@@ -1017,7 +1013,7 @@ static struct svc_xprt_ops svc_tcp_ops = { ...@@ -1017,7 +1013,7 @@ static struct svc_xprt_ops svc_tcp_ops = {
.xpo_recvfrom = svc_tcp_recvfrom, .xpo_recvfrom = svc_tcp_recvfrom,
.xpo_sendto = svc_tcp_sendto, .xpo_sendto = svc_tcp_sendto,
.xpo_release_rqst = svc_release_skb, .xpo_release_rqst = svc_release_skb,
.xpo_detach = svc_sock_detach, .xpo_detach = svc_tcp_sock_detach,
.xpo_free = svc_sock_free, .xpo_free = svc_sock_free,
.xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr, .xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr,
.xpo_has_wspace = svc_tcp_has_wspace, .xpo_has_wspace = svc_tcp_has_wspace,
...@@ -1101,7 +1097,7 @@ void svc_sock_update_bufs(struct svc_serv *serv) ...@@ -1101,7 +1097,7 @@ void svc_sock_update_bufs(struct svc_serv *serv)
} }
spin_unlock_bh(&serv->sv_lock); spin_unlock_bh(&serv->sv_lock);
} }
EXPORT_SYMBOL(svc_sock_update_bufs); EXPORT_SYMBOL_GPL(svc_sock_update_bufs);
/* /*
* Initialize socket for RPC use and create svc_sock struct * Initialize socket for RPC use and create svc_sock struct
...@@ -1287,6 +1283,24 @@ static void svc_sock_detach(struct svc_xprt *xprt) ...@@ -1287,6 +1283,24 @@ static void svc_sock_detach(struct svc_xprt *xprt)
sk->sk_state_change = svsk->sk_ostate; sk->sk_state_change = svsk->sk_ostate;
sk->sk_data_ready = svsk->sk_odata; sk->sk_data_ready = svsk->sk_odata;
sk->sk_write_space = svsk->sk_owspace; sk->sk_write_space = svsk->sk_owspace;
if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
wake_up_interruptible(sk->sk_sleep);
}
/*
* Disconnect the socket, and reset the callbacks
*/
static void svc_tcp_sock_detach(struct svc_xprt *xprt)
{
struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt);
dprintk("svc: svc_tcp_sock_detach(%p)\n", svsk);
svc_sock_detach(xprt);
if (!test_bit(XPT_LISTENER, &xprt->xpt_flags))
kernel_sock_shutdown(svsk->sk_sock, SHUT_RDWR);
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment