Merge git://git.linux-nfs.org/pub/linux/nfs-2.6

* git://git.linux-nfs.org/pub/linux/nfs-2.6: (74 commits)
  NFS: unmark NFS direct I/O as experimental
  NFS: add comments clarifying the use of nfs_post_op_update()
  NFSv4: rpc_mkpipe creating socket inodes w/out sk buffers
  NFS: Use SEEK_END instead of hardcoded value
  NFSv4: When mounting with a port=0 argument, substitute port=2049
  NFSv4: Poll more aggressively when handling NFS4ERR_DELAY
  NFSv4: Handle the condition NFS4ERR_FILE_OPEN
  NFSv4: Retry lease recovery if it failed during a synchronous operation.
  NFS: Don't invalidate the symlink we just stuffed into the cache
  NFS: Make read() return an ESTALE if the file has been deleted
  NFSv4: It's perfectly legal for clp to be NULL here....
  NFS: nfs_lookup - don't hash dentry when optimising away the lookup
  SUNRPC: Fix Oops in pmap_getport_done
  SUNRPC: Add refcounting to the struct rpc_xprt
  SUNRPC: Clean up soft task error handling
  SUNRPC: Handle ENETUNREACH, EHOSTUNREACH and EHOSTDOWN socket errors
  SUNRPC: rpc_delay() should not clobber the rpc_task->tk_status
  Fix a referral error Oops
  NFS: NFS_ROOT should use the new rpc_create API
  NFS: Fix up compiler warnings on 64-bit platforms in client.c
  ...

Manually resolved conflict in net/sunrpc/xprtsock.c
This commit is contained in:
Linus Torvalds 2006-09-23 16:58:40 -07:00
commit 9f261e0113
56 changed files with 4041 additions and 2160 deletions

View File

@ -2734,6 +2734,18 @@ long blk_congestion_wait(int rw, long timeout)
EXPORT_SYMBOL(blk_congestion_wait); EXPORT_SYMBOL(blk_congestion_wait);
/**
* blk_congestion_end - wake up sleepers on a congestion queue
* @rw: READ or WRITE
*/
void blk_congestion_end(int rw)
{
wait_queue_head_t *wqh = &congestion_wqh[rw];
if (waitqueue_active(wqh))
wake_up(wqh);
}
/* /*
* Has to be called with the request spinlock acquired * Has to be called with the request spinlock acquired
*/ */

View File

@ -1471,8 +1471,8 @@ config NFS_V4
If unsure, say N. If unsure, say N.
config NFS_DIRECTIO config NFS_DIRECTIO
bool "Allow direct I/O on NFS files (EXPERIMENTAL)" bool "Allow direct I/O on NFS files"
depends on NFS_FS && EXPERIMENTAL depends on NFS_FS
help help
This option enables applications to perform uncached I/O on files This option enables applications to perform uncached I/O on files
in NFS file systems using the O_DIRECT open() flag. When O_DIRECT in NFS file systems using the O_DIRECT open() flag. When O_DIRECT

View File

@ -828,17 +828,19 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
* (or otherwise set) by the caller to indicate that it is now * (or otherwise set) by the caller to indicate that it is now
* in use by the dcache. * in use by the dcache.
*/ */
struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode) static struct dentry *__d_instantiate_unique(struct dentry *entry,
struct inode *inode)
{ {
struct dentry *alias; struct dentry *alias;
int len = entry->d_name.len; int len = entry->d_name.len;
const char *name = entry->d_name.name; const char *name = entry->d_name.name;
unsigned int hash = entry->d_name.hash; unsigned int hash = entry->d_name.hash;
BUG_ON(!list_empty(&entry->d_alias)); if (!inode) {
spin_lock(&dcache_lock); entry->d_inode = NULL;
if (!inode) return NULL;
goto do_negative; }
list_for_each_entry(alias, &inode->i_dentry, d_alias) { list_for_each_entry(alias, &inode->i_dentry, d_alias) {
struct qstr *qstr = &alias->d_name; struct qstr *qstr = &alias->d_name;
@ -851,19 +853,35 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
if (memcmp(qstr->name, name, len)) if (memcmp(qstr->name, name, len))
continue; continue;
dget_locked(alias); dget_locked(alias);
spin_unlock(&dcache_lock);
BUG_ON(!d_unhashed(alias));
iput(inode);
return alias; return alias;
} }
list_add(&entry->d_alias, &inode->i_dentry); list_add(&entry->d_alias, &inode->i_dentry);
do_negative:
entry->d_inode = inode; entry->d_inode = inode;
fsnotify_d_instantiate(entry, inode); fsnotify_d_instantiate(entry, inode);
spin_unlock(&dcache_lock);
security_d_instantiate(entry, inode);
return NULL; return NULL;
} }
struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
{
struct dentry *result;
BUG_ON(!list_empty(&entry->d_alias));
spin_lock(&dcache_lock);
result = __d_instantiate_unique(entry, inode);
spin_unlock(&dcache_lock);
if (!result) {
security_d_instantiate(entry, inode);
return NULL;
}
BUG_ON(!d_unhashed(result));
iput(inode);
return result;
}
EXPORT_SYMBOL(d_instantiate_unique); EXPORT_SYMBOL(d_instantiate_unique);
/** /**
@ -1235,6 +1253,11 @@ static void __d_rehash(struct dentry * entry, struct hlist_head *list)
hlist_add_head_rcu(&entry->d_hash, list); hlist_add_head_rcu(&entry->d_hash, list);
} }
static void _d_rehash(struct dentry * entry)
{
__d_rehash(entry, d_hash(entry->d_parent, entry->d_name.hash));
}
/** /**
* d_rehash - add an entry back to the hash * d_rehash - add an entry back to the hash
* @entry: dentry to add to the hash * @entry: dentry to add to the hash
@ -1244,11 +1267,9 @@ static void __d_rehash(struct dentry * entry, struct hlist_head *list)
void d_rehash(struct dentry * entry) void d_rehash(struct dentry * entry)
{ {
struct hlist_head *list = d_hash(entry->d_parent, entry->d_name.hash);
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&entry->d_lock); spin_lock(&entry->d_lock);
__d_rehash(entry, list); _d_rehash(entry);
spin_unlock(&entry->d_lock); spin_unlock(&entry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
@ -1386,6 +1407,120 @@ already_unhashed:
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
} }
/*
* Prepare an anonymous dentry for life in the superblock's dentry tree as a
* named dentry in place of the dentry to be replaced.
*/
static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
{
struct dentry *dparent, *aparent;
switch_names(dentry, anon);
do_switch(dentry->d_name.len, anon->d_name.len);
do_switch(dentry->d_name.hash, anon->d_name.hash);
dparent = dentry->d_parent;
aparent = anon->d_parent;
dentry->d_parent = (aparent == anon) ? dentry : aparent;
list_del(&dentry->d_u.d_child);
if (!IS_ROOT(dentry))
list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
else
INIT_LIST_HEAD(&dentry->d_u.d_child);
anon->d_parent = (dparent == dentry) ? anon : dparent;
list_del(&anon->d_u.d_child);
if (!IS_ROOT(anon))
list_add(&anon->d_u.d_child, &anon->d_parent->d_subdirs);
else
INIT_LIST_HEAD(&anon->d_u.d_child);
anon->d_flags &= ~DCACHE_DISCONNECTED;
}
/**
* d_materialise_unique - introduce an inode into the tree
* @dentry: candidate dentry
* @inode: inode to bind to the dentry, to which aliases may be attached
*
* Introduces an dentry into the tree, substituting an extant disconnected
* root directory alias in its place if there is one
*/
struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
{
struct dentry *alias, *actual;
BUG_ON(!d_unhashed(dentry));
spin_lock(&dcache_lock);
if (!inode) {
actual = dentry;
dentry->d_inode = NULL;
goto found_lock;
}
/* See if a disconnected directory already exists as an anonymous root
* that we should splice into the tree instead */
if (S_ISDIR(inode->i_mode) && (alias = __d_find_alias(inode, 1))) {
spin_lock(&alias->d_lock);
/* Is this a mountpoint that we could splice into our tree? */
if (IS_ROOT(alias))
goto connect_mountpoint;
if (alias->d_name.len == dentry->d_name.len &&
alias->d_parent == dentry->d_parent &&
memcmp(alias->d_name.name,
dentry->d_name.name,
dentry->d_name.len) == 0)
goto replace_with_alias;
spin_unlock(&alias->d_lock);
/* Doh! Seem to be aliasing directories for some reason... */
dput(alias);
}
/* Add a unique reference */
actual = __d_instantiate_unique(dentry, inode);
if (!actual)
actual = dentry;
else if (unlikely(!d_unhashed(actual)))
goto shouldnt_be_hashed;
found_lock:
spin_lock(&actual->d_lock);
found:
_d_rehash(actual);
spin_unlock(&actual->d_lock);
spin_unlock(&dcache_lock);
if (actual == dentry) {
security_d_instantiate(dentry, inode);
return NULL;
}
iput(inode);
return actual;
/* Convert the anonymous/root alias into an ordinary dentry */
connect_mountpoint:
__d_materialise_dentry(dentry, alias);
/* Replace the candidate dentry with the alias in the tree */
replace_with_alias:
__d_drop(alias);
actual = alias;
goto found;
shouldnt_be_hashed:
spin_unlock(&dcache_lock);
BUG();
goto shouldnt_be_hashed;
}
/** /**
* d_path - return the path of a dentry * d_path - return the path of a dentry
* @dentry: dentry to report * @dentry: dentry to report
@ -1784,6 +1919,7 @@ EXPORT_SYMBOL(d_instantiate);
EXPORT_SYMBOL(d_invalidate); EXPORT_SYMBOL(d_invalidate);
EXPORT_SYMBOL(d_lookup); EXPORT_SYMBOL(d_lookup);
EXPORT_SYMBOL(d_move); EXPORT_SYMBOL(d_move);
EXPORT_SYMBOL_GPL(d_materialise_unique);
EXPORT_SYMBOL(d_path); EXPORT_SYMBOL(d_path);
EXPORT_SYMBOL(d_prune_aliases); EXPORT_SYMBOL(d_prune_aliases);
EXPORT_SYMBOL(d_rehash); EXPORT_SYMBOL(d_rehash);

View File

@ -151,11 +151,13 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req)
int int
nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
{ {
struct rpc_clnt *client = NFS_CLIENT(inode);
struct sockaddr_in addr;
struct nlm_host *host; struct nlm_host *host;
struct nlm_rqst *call; struct nlm_rqst *call;
sigset_t oldset; sigset_t oldset;
unsigned long flags; unsigned long flags;
int status, proto, vers; int status, vers;
vers = (NFS_PROTO(inode)->version == 3) ? 4 : 1; vers = (NFS_PROTO(inode)->version == 3) ? 4 : 1;
if (NFS_PROTO(inode)->version > 3) { if (NFS_PROTO(inode)->version > 3) {
@ -163,10 +165,8 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
return -ENOLCK; return -ENOLCK;
} }
/* Retrieve transport protocol from NFS client */ rpc_peeraddr(client, (struct sockaddr *) &addr, sizeof(addr));
proto = NFS_CLIENT(inode)->cl_xprt->prot; host = nlmclnt_lookup_host(&addr, client->cl_xprt->prot, vers);
host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers);
if (host == NULL) if (host == NULL)
return -ENOLCK; return -ENOLCK;

View File

@ -26,7 +26,6 @@
#define NLM_HOST_REBIND (60 * HZ) #define NLM_HOST_REBIND (60 * HZ)
#define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ) #define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ)
#define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ) #define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ)
#define NLM_HOST_ADDR(sv) (&(sv)->s_nlmclnt->cl_xprt->addr)
static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH]; static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH];
static unsigned long next_gc; static unsigned long next_gc;
@ -167,7 +166,6 @@ struct rpc_clnt *
nlm_bind_host(struct nlm_host *host) nlm_bind_host(struct nlm_host *host)
{ {
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
struct rpc_xprt *xprt;
dprintk("lockd: nlm_bind_host(%08x)\n", dprintk("lockd: nlm_bind_host(%08x)\n",
(unsigned)ntohl(host->h_addr.sin_addr.s_addr)); (unsigned)ntohl(host->h_addr.sin_addr.s_addr));
@ -179,7 +177,6 @@ nlm_bind_host(struct nlm_host *host)
* RPC rebind is required * RPC rebind is required
*/ */
if ((clnt = host->h_rpcclnt) != NULL) { if ((clnt = host->h_rpcclnt) != NULL) {
xprt = clnt->cl_xprt;
if (time_after_eq(jiffies, host->h_nextrebind)) { if (time_after_eq(jiffies, host->h_nextrebind)) {
rpc_force_rebind(clnt); rpc_force_rebind(clnt);
host->h_nextrebind = jiffies + NLM_HOST_REBIND; host->h_nextrebind = jiffies + NLM_HOST_REBIND;
@ -187,31 +184,37 @@ nlm_bind_host(struct nlm_host *host)
host->h_nextrebind - jiffies); host->h_nextrebind - jiffies);
} }
} else { } else {
xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL); unsigned long increment = nlmsvc_timeout * HZ;
if (IS_ERR(xprt)) struct rpc_timeout timeparms = {
goto forgetit; .to_initval = increment,
.to_increment = increment,
.to_maxval = increment * 6UL,
.to_retries = 5U,
};
struct rpc_create_args args = {
.protocol = host->h_proto,
.address = (struct sockaddr *)&host->h_addr,
.addrsize = sizeof(host->h_addr),
.timeout = &timeparms,
.servername = host->h_name,
.program = &nlm_program,
.version = host->h_version,
.authflavor = RPC_AUTH_UNIX,
.flags = (RPC_CLNT_CREATE_HARDRTRY |
RPC_CLNT_CREATE_AUTOBIND),
};
xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout); clnt = rpc_create(&args);
xprt->resvport = 1; /* NLM requires a reserved port */ if (!IS_ERR(clnt))
host->h_rpcclnt = clnt;
/* Existing NLM servers accept AUTH_UNIX only */ else {
clnt = rpc_new_client(xprt, host->h_name, &nlm_program, printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
host->h_version, RPC_AUTH_UNIX); clnt = NULL;
if (IS_ERR(clnt)) }
goto forgetit;
clnt->cl_autobind = 1; /* turn on pmap queries */
clnt->cl_softrtry = 1; /* All queries are soft */
host->h_rpcclnt = clnt;
} }
mutex_unlock(&host->h_mutex); mutex_unlock(&host->h_mutex);
return clnt; return clnt;
forgetit:
printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
mutex_unlock(&host->h_mutex);
return NULL;
} }
/* /*

View File

@ -109,30 +109,23 @@ nsm_unmonitor(struct nlm_host *host)
static struct rpc_clnt * static struct rpc_clnt *
nsm_create(void) nsm_create(void)
{ {
struct rpc_xprt *xprt; struct sockaddr_in sin = {
struct rpc_clnt *clnt; .sin_family = AF_INET,
struct sockaddr_in sin; .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
.sin_port = 0,
};
struct rpc_create_args args = {
.protocol = IPPROTO_UDP,
.address = (struct sockaddr *)&sin,
.addrsize = sizeof(sin),
.servername = "localhost",
.program = &nsm_program,
.version = SM_VERSION,
.authflavor = RPC_AUTH_NULL,
.flags = (RPC_CLNT_CREATE_ONESHOT),
};
sin.sin_family = AF_INET; return rpc_create(&args);
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sin.sin_port = 0;
xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL);
if (IS_ERR(xprt))
return (struct rpc_clnt *)xprt;
xprt->resvport = 1; /* NSM requires a reserved port */
clnt = rpc_create_client(xprt, "localhost",
&nsm_program, SM_VERSION,
RPC_AUTH_NULL);
if (IS_ERR(clnt))
goto out_err;
clnt->cl_softrtry = 1;
clnt->cl_oneshot = 1;
return clnt;
out_err:
return clnt;
} }
/* /*

View File

@ -4,9 +4,9 @@
obj-$(CONFIG_NFS_FS) += nfs.o obj-$(CONFIG_NFS_FS) += nfs.o
nfs-y := dir.o file.o inode.o super.o nfs2xdr.o pagelist.o \ nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \
proc.o read.o symlink.o unlink.o write.o \ pagelist.o proc.o read.o symlink.o unlink.o \
namespace.o write.o namespace.o
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o

View File

@ -19,6 +19,7 @@
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "callback.h" #include "callback.h"
#include "internal.h"
#define NFSDBG_FACILITY NFSDBG_CALLBACK #define NFSDBG_FACILITY NFSDBG_CALLBACK
@ -36,6 +37,21 @@ static struct svc_program nfs4_callback_program;
unsigned int nfs_callback_set_tcpport; unsigned int nfs_callback_set_tcpport;
unsigned short nfs_callback_tcpport; unsigned short nfs_callback_tcpport;
static const int nfs_set_port_min = 0;
static const int nfs_set_port_max = 65535;
static int param_set_port(const char *val, struct kernel_param *kp)
{
char *endp;
int num = simple_strtol(val, &endp, 0);
if (endp == val || *endp || num < nfs_set_port_min || num > nfs_set_port_max)
return -EINVAL;
*((int *)kp->arg) = num;
return 0;
}
module_param_call(callback_tcpport, param_set_port, param_get_int,
&nfs_callback_set_tcpport, 0644);
/* /*
* This is the callback kernel thread. * This is the callback kernel thread.
@ -134,10 +150,8 @@ out_err:
/* /*
* Kill the server process if it is not already up. * Kill the server process if it is not already up.
*/ */
int nfs_callback_down(void) void nfs_callback_down(void)
{ {
int ret = 0;
lock_kernel(); lock_kernel();
mutex_lock(&nfs_callback_mutex); mutex_lock(&nfs_callback_mutex);
nfs_callback_info.users--; nfs_callback_info.users--;
@ -149,20 +163,19 @@ int nfs_callback_down(void)
} while (wait_for_completion_timeout(&nfs_callback_info.stopped, 5*HZ) == 0); } while (wait_for_completion_timeout(&nfs_callback_info.stopped, 5*HZ) == 0);
mutex_unlock(&nfs_callback_mutex); mutex_unlock(&nfs_callback_mutex);
unlock_kernel(); unlock_kernel();
return ret;
} }
static int nfs_callback_authenticate(struct svc_rqst *rqstp) static int nfs_callback_authenticate(struct svc_rqst *rqstp)
{ {
struct in_addr *addr = &rqstp->rq_addr.sin_addr; struct sockaddr_in *addr = &rqstp->rq_addr;
struct nfs4_client *clp; struct nfs_client *clp;
/* Don't talk to strangers */ /* Don't talk to strangers */
clp = nfs4_find_client(addr); clp = nfs_find_client(addr, 4);
if (clp == NULL) if (clp == NULL)
return SVC_DROP; return SVC_DROP;
dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr)); dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr->sin_addr));
nfs4_put_client(clp); nfs_put_client(clp);
switch (rqstp->rq_authop->flavour) { switch (rqstp->rq_authop->flavour) {
case RPC_AUTH_NULL: case RPC_AUTH_NULL:
if (rqstp->rq_proc != CB_NULL) if (rqstp->rq_proc != CB_NULL)

View File

@ -62,8 +62,13 @@ struct cb_recallargs {
extern unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res); extern unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res);
extern unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy); extern unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
#ifdef CONFIG_NFS_V4
extern int nfs_callback_up(void); extern int nfs_callback_up(void);
extern int nfs_callback_down(void); extern void nfs_callback_down(void);
#else
#define nfs_callback_up() (0)
#define nfs_callback_down() do {} while(0)
#endif
extern unsigned int nfs_callback_set_tcpport; extern unsigned int nfs_callback_set_tcpport;
extern unsigned short nfs_callback_tcpport; extern unsigned short nfs_callback_tcpport;

View File

@ -10,19 +10,20 @@
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "callback.h" #include "callback.h"
#include "delegation.h" #include "delegation.h"
#include "internal.h"
#define NFSDBG_FACILITY NFSDBG_CALLBACK #define NFSDBG_FACILITY NFSDBG_CALLBACK
unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res) unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res)
{ {
struct nfs4_client *clp; struct nfs_client *clp;
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct nfs_inode *nfsi; struct nfs_inode *nfsi;
struct inode *inode; struct inode *inode;
res->bitmap[0] = res->bitmap[1] = 0; res->bitmap[0] = res->bitmap[1] = 0;
res->status = htonl(NFS4ERR_BADHANDLE); res->status = htonl(NFS4ERR_BADHANDLE);
clp = nfs4_find_client(&args->addr->sin_addr); clp = nfs_find_client(args->addr, 4);
if (clp == NULL) if (clp == NULL)
goto out; goto out;
inode = nfs_delegation_find_inode(clp, &args->fh); inode = nfs_delegation_find_inode(clp, &args->fh);
@ -48,7 +49,7 @@ out_iput:
up_read(&nfsi->rwsem); up_read(&nfsi->rwsem);
iput(inode); iput(inode);
out_putclient: out_putclient:
nfs4_put_client(clp); nfs_put_client(clp);
out: out:
dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res->status)); dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res->status));
return res->status; return res->status;
@ -56,12 +57,12 @@ out:
unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy) unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
{ {
struct nfs4_client *clp; struct nfs_client *clp;
struct inode *inode; struct inode *inode;
unsigned res; unsigned res;
res = htonl(NFS4ERR_BADHANDLE); res = htonl(NFS4ERR_BADHANDLE);
clp = nfs4_find_client(&args->addr->sin_addr); clp = nfs_find_client(args->addr, 4);
if (clp == NULL) if (clp == NULL)
goto out; goto out;
inode = nfs_delegation_find_inode(clp, &args->fh); inode = nfs_delegation_find_inode(clp, &args->fh);
@ -80,7 +81,7 @@ unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
} }
iput(inode); iput(inode);
out_putclient: out_putclient:
nfs4_put_client(clp); nfs_put_client(clp);
out: out:
dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res)); dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res));
return res; return res;

1448
fs/nfs/client.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,7 @@
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "delegation.h" #include "delegation.h"
#include "internal.h"
static struct nfs_delegation *nfs_alloc_delegation(void) static struct nfs_delegation *nfs_alloc_delegation(void)
{ {
@ -52,7 +53,7 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
/* kill_proc(fl->fl_pid, SIGLOST, 1); */ /* kill_proc(fl->fl_pid, SIGLOST, 1); */
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
nfs4_schedule_state_recovery(NFS_SERVER(inode)->nfs4_state); nfs4_schedule_state_recovery(NFS_SERVER(inode)->nfs_client);
goto out_err; goto out_err;
} }
} }
@ -114,7 +115,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st
*/ */
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
{ {
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state; struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
int status = 0; int status = 0;
@ -145,7 +146,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
sizeof(delegation->stateid)) != 0 || sizeof(delegation->stateid)) != 0 ||
delegation->type != nfsi->delegation->type) { delegation->type != nfsi->delegation->type) {
printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n", printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n",
__FUNCTION__, NIPQUAD(clp->cl_addr)); __FUNCTION__, NIPQUAD(clp->cl_addr.sin_addr));
status = -EIO; status = -EIO;
} }
} }
@ -176,7 +177,7 @@ static void nfs_msync_inode(struct inode *inode)
*/ */
int __nfs_inode_return_delegation(struct inode *inode) int __nfs_inode_return_delegation(struct inode *inode)
{ {
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state; struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
int res = 0; int res = 0;
@ -208,7 +209,7 @@ int __nfs_inode_return_delegation(struct inode *inode)
*/ */
void nfs_return_all_delegations(struct super_block *sb) void nfs_return_all_delegations(struct super_block *sb)
{ {
struct nfs4_client *clp = NFS_SB(sb)->nfs4_state; struct nfs_client *clp = NFS_SB(sb)->nfs_client;
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct inode *inode; struct inode *inode;
@ -232,7 +233,7 @@ restart:
int nfs_do_expire_all_delegations(void *ptr) int nfs_do_expire_all_delegations(void *ptr)
{ {
struct nfs4_client *clp = ptr; struct nfs_client *clp = ptr;
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct inode *inode; struct inode *inode;
@ -254,11 +255,11 @@ restart:
} }
out: out:
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
nfs4_put_client(clp); nfs_put_client(clp);
module_put_and_exit(0); module_put_and_exit(0);
} }
void nfs_expire_all_delegations(struct nfs4_client *clp) void nfs_expire_all_delegations(struct nfs_client *clp)
{ {
struct task_struct *task; struct task_struct *task;
@ -266,17 +267,17 @@ void nfs_expire_all_delegations(struct nfs4_client *clp)
atomic_inc(&clp->cl_count); atomic_inc(&clp->cl_count);
task = kthread_run(nfs_do_expire_all_delegations, clp, task = kthread_run(nfs_do_expire_all_delegations, clp,
"%u.%u.%u.%u-delegreturn", "%u.%u.%u.%u-delegreturn",
NIPQUAD(clp->cl_addr)); NIPQUAD(clp->cl_addr.sin_addr));
if (!IS_ERR(task)) if (!IS_ERR(task))
return; return;
nfs4_put_client(clp); nfs_put_client(clp);
module_put(THIS_MODULE); module_put(THIS_MODULE);
} }
/* /*
* Return all delegations following an NFS4ERR_CB_PATH_DOWN error. * Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
*/ */
void nfs_handle_cb_pathdown(struct nfs4_client *clp) void nfs_handle_cb_pathdown(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct inode *inode; struct inode *inode;
@ -299,7 +300,7 @@ restart:
struct recall_threadargs { struct recall_threadargs {
struct inode *inode; struct inode *inode;
struct nfs4_client *clp; struct nfs_client *clp;
const nfs4_stateid *stateid; const nfs4_stateid *stateid;
struct completion started; struct completion started;
@ -310,7 +311,7 @@ static int recall_thread(void *data)
{ {
struct recall_threadargs *args = (struct recall_threadargs *)data; struct recall_threadargs *args = (struct recall_threadargs *)data;
struct inode *inode = igrab(args->inode); struct inode *inode = igrab(args->inode);
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state; struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
@ -371,7 +372,7 @@ out_module_put:
/* /*
* Retrieve the inode associated with a delegation * Retrieve the inode associated with a delegation
*/ */
struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle) struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct inode *res = NULL; struct inode *res = NULL;
@ -389,7 +390,7 @@ struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nf
/* /*
* Mark all delegations as needing to be reclaimed * Mark all delegations as needing to be reclaimed
*/ */
void nfs_delegation_mark_reclaim(struct nfs4_client *clp) void nfs_delegation_mark_reclaim(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
@ -401,7 +402,7 @@ void nfs_delegation_mark_reclaim(struct nfs4_client *clp)
/* /*
* Reap all unclaimed delegations after reboot recovery is done * Reap all unclaimed delegations after reboot recovery is done
*/ */
void nfs_delegation_reap_unclaimed(struct nfs4_client *clp) void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation, *n; struct nfs_delegation *delegation, *n;
LIST_HEAD(head); LIST_HEAD(head);
@ -423,7 +424,7 @@ void nfs_delegation_reap_unclaimed(struct nfs4_client *clp)
int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode) int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
{ {
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state; struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
int res = 0; int res = 0;

View File

@ -29,13 +29,13 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st
int __nfs_inode_return_delegation(struct inode *inode); int __nfs_inode_return_delegation(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle); struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
void nfs_return_all_delegations(struct super_block *sb); void nfs_return_all_delegations(struct super_block *sb);
void nfs_expire_all_delegations(struct nfs4_client *clp); void nfs_expire_all_delegations(struct nfs_client *clp);
void nfs_handle_cb_pathdown(struct nfs4_client *clp); void nfs_handle_cb_pathdown(struct nfs_client *clp);
void nfs_delegation_mark_reclaim(struct nfs4_client *clp); void nfs_delegation_mark_reclaim(struct nfs_client *clp);
void nfs_delegation_reap_unclaimed(struct nfs4_client *clp); void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
/* NFSv4 delegation-related procedures */ /* NFSv4 delegation-related procedures */
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid); int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid);

View File

@ -30,7 +30,9 @@
#include <linux/nfs_mount.h> #include <linux/nfs_mount.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/pagevec.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/mount.h>
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "delegation.h" #include "delegation.h"
@ -870,14 +872,14 @@ int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd)
return (nd->intent.open.flags & O_EXCL) != 0; return (nd->intent.open.flags & O_EXCL) != 0;
} }
static inline int nfs_reval_fsid(struct inode *dir, static inline int nfs_reval_fsid(struct vfsmount *mnt, struct inode *dir,
struct nfs_fh *fh, struct nfs_fattr *fattr) struct nfs_fh *fh, struct nfs_fattr *fattr)
{ {
struct nfs_server *server = NFS_SERVER(dir); struct nfs_server *server = NFS_SERVER(dir);
if (!nfs_fsid_equal(&server->fsid, &fattr->fsid)) if (!nfs_fsid_equal(&server->fsid, &fattr->fsid))
/* Revalidate fsid on root dir */ /* Revalidate fsid on root dir */
return __nfs_revalidate_inode(server, dir->i_sb->s_root->d_inode); return __nfs_revalidate_inode(server, mnt->mnt_root->d_inode);
return 0; return 0;
} }
@ -902,9 +904,15 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
lock_kernel(); lock_kernel();
/* If we're doing an exclusive create, optimize away the lookup */ /*
if (nfs_is_exclusive_create(dir, nd)) * If we're doing an exclusive create, optimize away the lookup
goto no_entry; * but don't hash the dentry.
*/
if (nfs_is_exclusive_create(dir, nd)) {
d_instantiate(dentry, NULL);
res = NULL;
goto out_unlock;
}
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
if (error == -ENOENT) if (error == -ENOENT)
@ -913,7 +921,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
res = ERR_PTR(error); res = ERR_PTR(error);
goto out_unlock; goto out_unlock;
} }
error = nfs_reval_fsid(dir, &fhandle, &fattr); error = nfs_reval_fsid(nd->mnt, dir, &fhandle, &fattr);
if (error < 0) { if (error < 0) {
res = ERR_PTR(error); res = ERR_PTR(error);
goto out_unlock; goto out_unlock;
@ -922,8 +930,9 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
res = (struct dentry *)inode; res = (struct dentry *)inode;
if (IS_ERR(res)) if (IS_ERR(res))
goto out_unlock; goto out_unlock;
no_entry: no_entry:
res = d_add_unique(dentry, inode); res = d_materialise_unique(dentry, inode);
if (res != NULL) if (res != NULL)
dentry = res; dentry = res;
nfs_renew_times(dentry); nfs_renew_times(dentry);
@ -1117,11 +1126,13 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc)
dput(dentry); dput(dentry);
return NULL; return NULL;
} }
alias = d_add_unique(dentry, inode);
alias = d_materialise_unique(dentry, inode);
if (alias != NULL) { if (alias != NULL) {
dput(dentry); dput(dentry);
dentry = alias; dentry = alias;
} }
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
return dentry; return dentry;
@ -1143,23 +1154,22 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
struct inode *dir = dentry->d_parent->d_inode; struct inode *dir = dentry->d_parent->d_inode;
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
if (error) if (error)
goto out_err; return error;
} }
if (!(fattr->valid & NFS_ATTR_FATTR)) { if (!(fattr->valid & NFS_ATTR_FATTR)) {
struct nfs_server *server = NFS_SB(dentry->d_sb); struct nfs_server *server = NFS_SB(dentry->d_sb);
error = server->rpc_ops->getattr(server, fhandle, fattr); error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr);
if (error < 0) if (error < 0)
goto out_err; return error;
} }
inode = nfs_fhget(dentry->d_sb, fhandle, fattr); inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
error = PTR_ERR(inode); error = PTR_ERR(inode);
if (IS_ERR(inode)) if (IS_ERR(inode))
goto out_err; return error;
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
if (d_unhashed(dentry))
d_rehash(dentry);
return 0; return 0;
out_err:
d_drop(dentry);
return error;
} }
/* /*
@ -1440,48 +1450,82 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
return error; return error;
} }
static int /*
nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) * To create a symbolic link, most file systems instantiate a new inode,
* add a page to it containing the path, then write it out to the disk
* using prepare_write/commit_write.
*
* Unfortunately the NFS client can't create the in-core inode first
* because it needs a file handle to create an in-core inode (see
* fs/nfs/inode.c:nfs_fhget). We only have a file handle *after* the
* symlink request has completed on the server.
*
* So instead we allocate a raw page, copy the symname into it, then do
* the SYMLINK request with the page as the buffer. If it succeeds, we
* now have a new file handle and can instantiate an in-core NFS inode
* and move the raw page into its mapping.
*/
static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{ {
struct pagevec lru_pvec;
struct page *page;
char *kaddr;
struct iattr attr; struct iattr attr;
struct nfs_fattr sym_attr; unsigned int pathlen = strlen(symname);
struct nfs_fh sym_fh;
struct qstr qsymname;
int error; int error;
dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id, dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
dir->i_ino, dentry->d_name.name, symname); dir->i_ino, dentry->d_name.name, symname);
#ifdef NFS_PARANOIA if (pathlen > PAGE_SIZE)
if (dentry->d_inode) return -ENAMETOOLONG;
printk("nfs_proc_symlink: %s/%s not negative!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
/*
* Fill in the sattr for the call.
* Note: SunOS 4.1.2 crashes if the mode isn't initialized!
*/
attr.ia_valid = ATTR_MODE;
attr.ia_mode = S_IFLNK | S_IRWXUGO;
qsymname.name = symname; attr.ia_mode = S_IFLNK | S_IRWXUGO;
qsymname.len = strlen(symname); attr.ia_valid = ATTR_MODE;
lock_kernel(); lock_kernel();
nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname, page = alloc_page(GFP_KERNEL);
&attr, &sym_fh, &sym_attr); if (!page) {
nfs_end_data_update(dir); unlock_kernel();
if (!error) { return -ENOMEM;
error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
} else {
if (error == -EEXIST)
printk("nfs_proc_symlink: %s/%s already exists??\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
d_drop(dentry);
} }
kaddr = kmap_atomic(page, KM_USER0);
memcpy(kaddr, symname, pathlen);
if (pathlen < PAGE_SIZE)
memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
kunmap_atomic(kaddr, KM_USER0);
nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
nfs_end_data_update(dir);
if (error != 0) {
dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n",
dir->i_sb->s_id, dir->i_ino,
dentry->d_name.name, symname, error);
d_drop(dentry);
__free_page(page);
unlock_kernel();
return error;
}
/*
* No big deal if we can't add this page to the page cache here.
* READLINK will get the missing page from the server if needed.
*/
pagevec_init(&lru_pvec, 0);
if (!add_to_page_cache(page, dentry->d_inode->i_mapping, 0,
GFP_KERNEL)) {
if (!pagevec_add(&lru_pvec, page))
__pagevec_lru_add(&lru_pvec);
SetPageUptodate(page);
unlock_page(page);
} else
__free_page(page);
unlock_kernel(); unlock_kernel();
return error; return 0;
} }
static int static int
@ -1638,35 +1682,211 @@ out:
return error; return error;
} }
static DEFINE_SPINLOCK(nfs_access_lru_lock);
static LIST_HEAD(nfs_access_lru_list);
static atomic_long_t nfs_access_nr_entries;
static void nfs_access_free_entry(struct nfs_access_entry *entry)
{
put_rpccred(entry->cred);
kfree(entry);
smp_mb__before_atomic_dec();
atomic_long_dec(&nfs_access_nr_entries);
smp_mb__after_atomic_dec();
}
int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask)
{
LIST_HEAD(head);
struct nfs_inode *nfsi;
struct nfs_access_entry *cache;
spin_lock(&nfs_access_lru_lock);
restart:
list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) {
struct inode *inode;
if (nr_to_scan-- == 0)
break;
inode = igrab(&nfsi->vfs_inode);
if (inode == NULL)
continue;
spin_lock(&inode->i_lock);
if (list_empty(&nfsi->access_cache_entry_lru))
goto remove_lru_entry;
cache = list_entry(nfsi->access_cache_entry_lru.next,
struct nfs_access_entry, lru);
list_move(&cache->lru, &head);
rb_erase(&cache->rb_node, &nfsi->access_cache);
if (!list_empty(&nfsi->access_cache_entry_lru))
list_move_tail(&nfsi->access_cache_inode_lru,
&nfs_access_lru_list);
else {
remove_lru_entry:
list_del_init(&nfsi->access_cache_inode_lru);
clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags);
}
spin_unlock(&inode->i_lock);
iput(inode);
goto restart;
}
spin_unlock(&nfs_access_lru_lock);
while (!list_empty(&head)) {
cache = list_entry(head.next, struct nfs_access_entry, lru);
list_del(&cache->lru);
nfs_access_free_entry(cache);
}
return (atomic_long_read(&nfs_access_nr_entries) / 100) * sysctl_vfs_cache_pressure;
}
static void __nfs_access_zap_cache(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct rb_root *root_node = &nfsi->access_cache;
struct rb_node *n, *dispose = NULL;
struct nfs_access_entry *entry;
/* Unhook entries from the cache */
while ((n = rb_first(root_node)) != NULL) {
entry = rb_entry(n, struct nfs_access_entry, rb_node);
rb_erase(n, root_node);
list_del(&entry->lru);
n->rb_left = dispose;
dispose = n;
}
nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS;
spin_unlock(&inode->i_lock);
/* Now kill them all! */
while (dispose != NULL) {
n = dispose;
dispose = n->rb_left;
nfs_access_free_entry(rb_entry(n, struct nfs_access_entry, rb_node));
}
}
void nfs_access_zap_cache(struct inode *inode)
{
/* Remove from global LRU init */
if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_FLAGS(inode))) {
spin_lock(&nfs_access_lru_lock);
list_del_init(&NFS_I(inode)->access_cache_inode_lru);
spin_unlock(&nfs_access_lru_lock);
}
spin_lock(&inode->i_lock);
/* This will release the spinlock */
__nfs_access_zap_cache(inode);
}
static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, struct rpc_cred *cred)
{
struct rb_node *n = NFS_I(inode)->access_cache.rb_node;
struct nfs_access_entry *entry;
while (n != NULL) {
entry = rb_entry(n, struct nfs_access_entry, rb_node);
if (cred < entry->cred)
n = n->rb_left;
else if (cred > entry->cred)
n = n->rb_right;
else
return entry;
}
return NULL;
}
int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res) int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_access_entry *cache = &nfsi->cache_access; struct nfs_access_entry *cache;
int err = -ENOENT;
if (cache->cred != cred spin_lock(&inode->i_lock);
|| time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode)) if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)
|| (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)) goto out_zap;
return -ENOENT; cache = nfs_access_search_rbtree(inode, cred);
memcpy(res, cache, sizeof(*res)); if (cache == NULL)
return 0; goto out;
if (time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode)))
goto out_stale;
res->jiffies = cache->jiffies;
res->cred = cache->cred;
res->mask = cache->mask;
list_move_tail(&cache->lru, &nfsi->access_cache_entry_lru);
err = 0;
out:
spin_unlock(&inode->i_lock);
return err;
out_stale:
rb_erase(&cache->rb_node, &nfsi->access_cache);
list_del(&cache->lru);
spin_unlock(&inode->i_lock);
nfs_access_free_entry(cache);
return -ENOENT;
out_zap:
/* This will release the spinlock */
__nfs_access_zap_cache(inode);
return -ENOENT;
}
static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct rb_root *root_node = &nfsi->access_cache;
struct rb_node **p = &root_node->rb_node;
struct rb_node *parent = NULL;
struct nfs_access_entry *entry;
spin_lock(&inode->i_lock);
while (*p != NULL) {
parent = *p;
entry = rb_entry(parent, struct nfs_access_entry, rb_node);
if (set->cred < entry->cred)
p = &parent->rb_left;
else if (set->cred > entry->cred)
p = &parent->rb_right;
else
goto found;
}
rb_link_node(&set->rb_node, parent, p);
rb_insert_color(&set->rb_node, root_node);
list_add_tail(&set->lru, &nfsi->access_cache_entry_lru);
spin_unlock(&inode->i_lock);
return;
found:
rb_replace_node(parent, &set->rb_node, root_node);
list_add_tail(&set->lru, &nfsi->access_cache_entry_lru);
list_del(&entry->lru);
spin_unlock(&inode->i_lock);
nfs_access_free_entry(entry);
} }
void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set) void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL);
struct nfs_access_entry *cache = &nfsi->cache_access; if (cache == NULL)
return;
if (cache->cred != set->cred) { RB_CLEAR_NODE(&cache->rb_node);
if (cache->cred)
put_rpccred(cache->cred);
cache->cred = get_rpccred(set->cred);
}
/* FIXME: replace current access_cache BKL reliance with inode->i_lock */
spin_lock(&inode->i_lock);
nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS;
spin_unlock(&inode->i_lock);
cache->jiffies = set->jiffies; cache->jiffies = set->jiffies;
cache->cred = get_rpccred(set->cred);
cache->mask = set->mask; cache->mask = set->mask;
nfs_access_add_rbtree(inode, cache);
/* Update accounting */
smp_mb__before_atomic_inc();
atomic_long_inc(&nfs_access_nr_entries);
smp_mb__after_atomic_inc();
/* Add inode to global LRU list */
if (!test_and_set_bit(NFS_INO_ACL_LRU_SET, &NFS_FLAGS(inode))) {
spin_lock(&nfs_access_lru_lock);
list_add_tail(&NFS_I(inode)->access_cache_inode_lru, &nfs_access_lru_list);
spin_unlock(&nfs_access_lru_lock);
}
} }
static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)

View File

@ -111,7 +111,7 @@ nfs_file_open(struct inode *inode, struct file *filp)
nfs_inc_stats(inode, NFSIOS_VFSOPEN); nfs_inc_stats(inode, NFSIOS_VFSOPEN);
lock_kernel(); lock_kernel();
res = NFS_SERVER(inode)->rpc_ops->file_open(inode, filp); res = NFS_PROTO(inode)->file_open(inode, filp);
unlock_kernel(); unlock_kernel();
return res; return res;
} }
@ -157,7 +157,7 @@ force_reval:
static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
{ {
/* origin == SEEK_END => we must revalidate the cached file length */ /* origin == SEEK_END => we must revalidate the cached file length */
if (origin == 2) { if (origin == SEEK_END) {
struct inode *inode = filp->f_mapping->host; struct inode *inode = filp->f_mapping->host;
int retval = nfs_revalidate_file_size(inode, filp); int retval = nfs_revalidate_file_size(inode, filp);
if (retval < 0) if (retval < 0)

311
fs/nfs/getroot.c Normal file
View File

@ -0,0 +1,311 @@
/* getroot.c: get the root dentry for an NFS mount
*
* Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/stats.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/nfs4_mount.h>
#include <linux/lockd/bind.h>
#include <linux/smp_lock.h>
#include <linux/seq_file.h>
#include <linux/mount.h>
#include <linux/nfs_idmap.h>
#include <linux/vfs.h>
#include <linux/namei.h>
#include <linux/namespace.h>
#include <linux/security.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include "nfs4_fs.h"
#include "delegation.h"
#include "internal.h"
#define NFSDBG_FACILITY NFSDBG_CLIENT
#define NFS_PARANOIA 1
/*
* get an NFS2/NFS3 root dentry from the root filehandle
*/
struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
{
struct nfs_server *server = NFS_SB(sb);
struct nfs_fsinfo fsinfo;
struct nfs_fattr fattr;
struct dentry *mntroot;
struct inode *inode;
int error;
/* create a dummy root dentry with dummy inode for this superblock */
if (!sb->s_root) {
struct nfs_fh dummyfh;
struct dentry *root;
struct inode *iroot;
memset(&dummyfh, 0, sizeof(dummyfh));
memset(&fattr, 0, sizeof(fattr));
nfs_fattr_init(&fattr);
fattr.valid = NFS_ATTR_FATTR;
fattr.type = NFDIR;
fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR;
fattr.nlink = 2;
iroot = nfs_fhget(sb, &dummyfh, &fattr);
if (IS_ERR(iroot))
return ERR_PTR(PTR_ERR(iroot));
root = d_alloc_root(iroot);
if (!root) {
iput(iroot);
return ERR_PTR(-ENOMEM);
}
sb->s_root = root;
}
/* get the actual root for this mount */
fsinfo.fattr = &fattr;
error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
if (error < 0) {
dprintk("nfs_get_root: getattr error = %d\n", -error);
return ERR_PTR(error);
}
inode = nfs_fhget(sb, mntfh, fsinfo.fattr);
if (IS_ERR(inode)) {
dprintk("nfs_get_root: get root inode failed\n");
return ERR_PTR(PTR_ERR(inode));
}
/* root dentries normally start off anonymous and get spliced in later
* if the dentry tree reaches them; however if the dentry already
* exists, we'll pick it up at this point and use it as the root
*/
mntroot = d_alloc_anon(inode);
if (!mntroot) {
iput(inode);
dprintk("nfs_get_root: get root dentry failed\n");
return ERR_PTR(-ENOMEM);
}
security_d_instantiate(mntroot, inode);
if (!mntroot->d_op)
mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;
return mntroot;
}
#ifdef CONFIG_NFS_V4
/*
* Do a simple pathwalk from the root FH of the server to the nominated target
* of the mountpoint
* - give error on symlinks
* - give error on ".." occurring in the path
* - follow traversals
*/
int nfs4_path_walk(struct nfs_server *server,
struct nfs_fh *mntfh,
const char *path)
{
struct nfs_fsinfo fsinfo;
struct nfs_fattr fattr;
struct nfs_fh lastfh;
struct qstr name;
int ret;
//int referral_count = 0;
dprintk("--> nfs4_path_walk(,,%s)\n", path);
fsinfo.fattr = &fattr;
nfs_fattr_init(&fattr);
if (*path++ != '/') {
dprintk("nfs4_get_root: Path does not begin with a slash\n");
return -EINVAL;
}
/* Start by getting the root filehandle from the server */
ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
if (ret < 0) {
dprintk("nfs4_get_root: getroot error = %d\n", -ret);
return ret;
}
if (fattr.type != NFDIR) {
printk(KERN_ERR "nfs4_get_root:"
" getroot encountered non-directory\n");
return -ENOTDIR;
}
if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
printk(KERN_ERR "nfs4_get_root:"
" getroot obtained referral\n");
return -EREMOTE;
}
next_component:
dprintk("Next: %s\n", path);
/* extract the next bit of the path */
if (!*path)
goto path_walk_complete;
name.name = path;
while (*path && *path != '/')
path++;
name.len = path - (const char *) name.name;
eat_dot_dir:
while (*path == '/')
path++;
if (path[0] == '.' && (path[1] == '/' || !path[1])) {
path += 2;
goto eat_dot_dir;
}
if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2])
) {
printk(KERN_ERR "nfs4_get_root:"
" Mount path contains reference to \"..\"\n");
return -EINVAL;
}
/* lookup the next FH in the sequence */
memcpy(&lastfh, mntfh, sizeof(lastfh));
dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path);
ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name,
mntfh, &fattr);
if (ret < 0) {
dprintk("nfs4_get_root: getroot error = %d\n", -ret);
return ret;
}
if (fattr.type != NFDIR) {
printk(KERN_ERR "nfs4_get_root:"
" lookupfh encountered non-directory\n");
return -ENOTDIR;
}
if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
printk(KERN_ERR "nfs4_get_root:"
" lookupfh obtained referral\n");
return -EREMOTE;
}
goto next_component;
path_walk_complete:
memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid));
dprintk("<-- nfs4_path_walk() = 0\n");
return 0;
}
/*
* get an NFS4 root dentry from the root filehandle
*/
struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
{
struct nfs_server *server = NFS_SB(sb);
struct nfs_fattr fattr;
struct dentry *mntroot;
struct inode *inode;
int error;
dprintk("--> nfs4_get_root()\n");
/* create a dummy root dentry with dummy inode for this superblock */
if (!sb->s_root) {
struct nfs_fh dummyfh;
struct dentry *root;
struct inode *iroot;
memset(&dummyfh, 0, sizeof(dummyfh));
memset(&fattr, 0, sizeof(fattr));
nfs_fattr_init(&fattr);
fattr.valid = NFS_ATTR_FATTR;
fattr.type = NFDIR;
fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR;
fattr.nlink = 2;
iroot = nfs_fhget(sb, &dummyfh, &fattr);
if (IS_ERR(iroot))
return ERR_PTR(PTR_ERR(iroot));
root = d_alloc_root(iroot);
if (!root) {
iput(iroot);
return ERR_PTR(-ENOMEM);
}
sb->s_root = root;
}
/* get the info about the server and filesystem */
error = nfs4_server_capabilities(server, mntfh);
if (error < 0) {
dprintk("nfs_get_root: getcaps error = %d\n",
-error);
return ERR_PTR(error);
}
/* get the actual root for this mount */
error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr);
if (error < 0) {
dprintk("nfs_get_root: getattr error = %d\n", -error);
return ERR_PTR(error);
}
inode = nfs_fhget(sb, mntfh, &fattr);
if (IS_ERR(inode)) {
dprintk("nfs_get_root: get root inode failed\n");
return ERR_PTR(PTR_ERR(inode));
}
/* root dentries normally start off anonymous and get spliced in later
* if the dentry tree reaches them; however if the dentry already
* exists, we'll pick it up at this point and use it as the root
*/
mntroot = d_alloc_anon(inode);
if (!mntroot) {
iput(inode);
dprintk("nfs_get_root: get root dentry failed\n");
return ERR_PTR(-ENOMEM);
}
security_d_instantiate(mntroot, inode);
if (!mntroot->d_op)
mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;
dprintk("<-- nfs4_get_root()\n");
return mntroot;
}
#endif /* CONFIG_NFS_V4 */

View File

@ -57,6 +57,20 @@
/* Default cache timeout is 10 minutes */ /* Default cache timeout is 10 minutes */
unsigned int nfs_idmap_cache_timeout = 600 * HZ; unsigned int nfs_idmap_cache_timeout = 600 * HZ;
static int param_set_idmap_timeout(const char *val, struct kernel_param *kp)
{
char *endp;
int num = simple_strtol(val, &endp, 0);
int jif = num * HZ;
if (endp == val || *endp || num < 0 || jif < num)
return -EINVAL;
*((int *)kp->arg) = jif;
return 0;
}
module_param_call(idmap_cache_timeout, param_set_idmap_timeout, param_get_int,
&nfs_idmap_cache_timeout, 0644);
struct idmap_hashent { struct idmap_hashent {
unsigned long ih_expires; unsigned long ih_expires;
__u32 ih_id; __u32 ih_id;
@ -70,7 +84,6 @@ struct idmap_hashtable {
}; };
struct idmap { struct idmap {
char idmap_path[48];
struct dentry *idmap_dentry; struct dentry *idmap_dentry;
wait_queue_head_t idmap_wq; wait_queue_head_t idmap_wq;
struct idmap_msg idmap_im; struct idmap_msg idmap_im;
@ -94,24 +107,23 @@ static struct rpc_pipe_ops idmap_upcall_ops = {
.destroy_msg = idmap_pipe_destroy_msg, .destroy_msg = idmap_pipe_destroy_msg,
}; };
void int
nfs_idmap_new(struct nfs4_client *clp) nfs_idmap_new(struct nfs_client *clp)
{ {
struct idmap *idmap; struct idmap *idmap;
int error;
BUG_ON(clp->cl_idmap != NULL);
if (clp->cl_idmap != NULL)
return;
if ((idmap = kzalloc(sizeof(*idmap), GFP_KERNEL)) == NULL) if ((idmap = kzalloc(sizeof(*idmap), GFP_KERNEL)) == NULL)
return; return -ENOMEM;
snprintf(idmap->idmap_path, sizeof(idmap->idmap_path), idmap->idmap_dentry = rpc_mkpipe(clp->cl_rpcclient->cl_dentry, "idmap",
"%s/idmap", clp->cl_rpcclient->cl_pathname);
idmap->idmap_dentry = rpc_mkpipe(idmap->idmap_path,
idmap, &idmap_upcall_ops, 0); idmap, &idmap_upcall_ops, 0);
if (IS_ERR(idmap->idmap_dentry)) { if (IS_ERR(idmap->idmap_dentry)) {
error = PTR_ERR(idmap->idmap_dentry);
kfree(idmap); kfree(idmap);
return; return error;
} }
mutex_init(&idmap->idmap_lock); mutex_init(&idmap->idmap_lock);
@ -121,10 +133,11 @@ nfs_idmap_new(struct nfs4_client *clp)
idmap->idmap_group_hash.h_type = IDMAP_TYPE_GROUP; idmap->idmap_group_hash.h_type = IDMAP_TYPE_GROUP;
clp->cl_idmap = idmap; clp->cl_idmap = idmap;
return 0;
} }
void void
nfs_idmap_delete(struct nfs4_client *clp) nfs_idmap_delete(struct nfs_client *clp)
{ {
struct idmap *idmap = clp->cl_idmap; struct idmap *idmap = clp->cl_idmap;
@ -477,27 +490,27 @@ static unsigned int fnvhash32(const void *buf, size_t buflen)
return (hash); return (hash);
} }
int nfs_map_name_to_uid(struct nfs4_client *clp, const char *name, size_t namelen, __u32 *uid) int nfs_map_name_to_uid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *uid)
{ {
struct idmap *idmap = clp->cl_idmap; struct idmap *idmap = clp->cl_idmap;
return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid); return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid);
} }
int nfs_map_group_to_gid(struct nfs4_client *clp, const char *name, size_t namelen, __u32 *uid) int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *uid)
{ {
struct idmap *idmap = clp->cl_idmap; struct idmap *idmap = clp->cl_idmap;
return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid);
} }
int nfs_map_uid_to_name(struct nfs4_client *clp, __u32 uid, char *buf) int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf)
{ {
struct idmap *idmap = clp->cl_idmap; struct idmap *idmap = clp->cl_idmap;
return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf);
} }
int nfs_map_gid_to_group(struct nfs4_client *clp, __u32 uid, char *buf) int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf)
{ {
struct idmap *idmap = clp->cl_idmap; struct idmap *idmap = clp->cl_idmap;

View File

@ -76,19 +76,14 @@ int nfs_write_inode(struct inode *inode, int sync)
void nfs_clear_inode(struct inode *inode) void nfs_clear_inode(struct inode *inode)
{ {
struct nfs_inode *nfsi = NFS_I(inode);
struct rpc_cred *cred;
/* /*
* The following should never happen... * The following should never happen...
*/ */
BUG_ON(nfs_have_writebacks(inode)); BUG_ON(nfs_have_writebacks(inode));
BUG_ON (!list_empty(&nfsi->open_files)); BUG_ON(!list_empty(&NFS_I(inode)->open_files));
BUG_ON(atomic_read(&NFS_I(inode)->data_updates) != 0);
nfs_zap_acl_cache(inode); nfs_zap_acl_cache(inode);
cred = nfsi->cache_access.cred; nfs_access_zap_cache(inode);
if (cred)
put_rpccred(cred);
BUG_ON(atomic_read(&nfsi->data_updates) != 0);
} }
/** /**
@ -242,13 +237,13 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
/* Why so? Because we want revalidate for devices/FIFOs, and /* Why so? Because we want revalidate for devices/FIFOs, and
* that's precisely what we have in nfs_file_inode_operations. * that's precisely what we have in nfs_file_inode_operations.
*/ */
inode->i_op = NFS_SB(sb)->rpc_ops->file_inode_ops; inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->file_inode_ops;
if (S_ISREG(inode->i_mode)) { if (S_ISREG(inode->i_mode)) {
inode->i_fop = &nfs_file_operations; inode->i_fop = &nfs_file_operations;
inode->i_data.a_ops = &nfs_file_aops; inode->i_data.a_ops = &nfs_file_aops;
inode->i_data.backing_dev_info = &NFS_SB(sb)->backing_dev_info; inode->i_data.backing_dev_info = &NFS_SB(sb)->backing_dev_info;
} else if (S_ISDIR(inode->i_mode)) { } else if (S_ISDIR(inode->i_mode)) {
inode->i_op = NFS_SB(sb)->rpc_ops->dir_inode_ops; inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
inode->i_fop = &nfs_dir_operations; inode->i_fop = &nfs_dir_operations;
if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS) if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)
&& fattr->size <= NFS_LIMIT_READDIRPLUS) && fattr->size <= NFS_LIMIT_READDIRPLUS)
@ -290,7 +285,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies; nfsi->attrtimeo_timestamp = jiffies;
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
nfsi->cache_access.cred = NULL; nfsi->access_cache = RB_ROOT;
unlock_new_inode(inode); unlock_new_inode(inode);
} else } else
@ -722,13 +717,11 @@ void nfs_end_data_update(struct inode *inode)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
if (!nfs_have_delegation(inode, FMODE_READ)) { /* Directories: invalidate page cache */
/* Directories and symlinks: invalidate page cache */ if (S_ISDIR(inode->i_mode)) {
if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) { spin_lock(&inode->i_lock);
spin_lock(&inode->i_lock); nfsi->cache_validity |= NFS_INO_INVALID_DATA;
nfsi->cache_validity |= NFS_INO_INVALID_DATA; spin_unlock(&inode->i_lock);
spin_unlock(&inode->i_lock);
}
} }
nfsi->cache_change_attribute = jiffies; nfsi->cache_change_attribute = jiffies;
atomic_dec(&nfsi->data_updates); atomic_dec(&nfsi->data_updates);
@ -847,6 +840,12 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
* *
* After an operation that has changed the inode metadata, mark the * After an operation that has changed the inode metadata, mark the
* attribute cache as being invalid, then try to update it. * attribute cache as being invalid, then try to update it.
*
* NB: if the server didn't return any post op attributes, this
* function will force the retrieval of attributes before the next
* NFS request. Thus it should be used only for operations that
* are expected to change one or more attributes, to avoid
* unnecessary NFS requests and trips through nfs_update_inode().
*/ */
int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr) int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
{ {
@ -1025,7 +1024,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
out_fileid: out_fileid:
printk(KERN_ERR "NFS: server %s error: fileid changed\n" printk(KERN_ERR "NFS: server %s error: fileid changed\n"
"fsid %s: expected fileid 0x%Lx, got 0x%Lx\n", "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n",
NFS_SERVER(inode)->hostname, inode->i_sb->s_id, NFS_SERVER(inode)->nfs_client->cl_hostname, inode->i_sb->s_id,
(long long)nfsi->fileid, (long long)fattr->fileid); (long long)nfsi->fileid, (long long)fattr->fileid);
goto out_err; goto out_err;
} }
@ -1109,6 +1108,8 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
INIT_LIST_HEAD(&nfsi->dirty); INIT_LIST_HEAD(&nfsi->dirty);
INIT_LIST_HEAD(&nfsi->commit); INIT_LIST_HEAD(&nfsi->commit);
INIT_LIST_HEAD(&nfsi->open_files); INIT_LIST_HEAD(&nfsi->open_files);
INIT_LIST_HEAD(&nfsi->access_cache_entry_lru);
INIT_LIST_HEAD(&nfsi->access_cache_inode_lru);
INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
atomic_set(&nfsi->data_updates, 0); atomic_set(&nfsi->data_updates, 0);
nfsi->ndirty = 0; nfsi->ndirty = 0;
@ -1144,6 +1145,10 @@ static int __init init_nfs_fs(void)
{ {
int err; int err;
err = nfs_fs_proc_init();
if (err)
goto out5;
err = nfs_init_nfspagecache(); err = nfs_init_nfspagecache();
if (err) if (err)
goto out4; goto out4;
@ -1184,6 +1189,8 @@ out2:
out3: out3:
nfs_destroy_nfspagecache(); nfs_destroy_nfspagecache();
out4: out4:
nfs_fs_proc_exit();
out5:
return err; return err;
} }
@ -1198,6 +1205,7 @@ static void __exit exit_nfs_fs(void)
rpc_proc_unregister("nfs"); rpc_proc_unregister("nfs");
#endif #endif
unregister_nfs_fs(); unregister_nfs_fs();
nfs_fs_proc_exit();
} }
/* Not quite true; I just maintain it */ /* Not quite true; I just maintain it */

View File

@ -4,6 +4,18 @@
#include <linux/mount.h> #include <linux/mount.h>
struct nfs_string;
struct nfs_mount_data;
struct nfs4_mount_data;
/* Maximum number of readahead requests
* FIXME: this should really be a sysctl so that users may tune it to suit
* their needs. People that do NFS over a slow network, might for
* instance want to reduce it to something closer to 1 for improved
* interactive response.
*/
#define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1)
struct nfs_clone_mount { struct nfs_clone_mount {
const struct super_block *sb; const struct super_block *sb;
const struct dentry *dentry; const struct dentry *dentry;
@ -15,7 +27,40 @@ struct nfs_clone_mount {
rpc_authflavor_t authflavor; rpc_authflavor_t authflavor;
}; };
/* namespace-nfs4.c */ /* client.c */
extern struct rpc_program nfs_program;
extern void nfs_put_client(struct nfs_client *);
extern struct nfs_client *nfs_find_client(const struct sockaddr_in *, int);
extern struct nfs_server *nfs_create_server(const struct nfs_mount_data *,
struct nfs_fh *);
extern struct nfs_server *nfs4_create_server(const struct nfs4_mount_data *,
const char *,
const struct sockaddr_in *,
const char *,
const char *,
rpc_authflavor_t,
struct nfs_fh *);
extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *,
struct nfs_fh *);
extern void nfs_free_server(struct nfs_server *server);
extern struct nfs_server *nfs_clone_server(struct nfs_server *,
struct nfs_fh *,
struct nfs_fattr *);
#ifdef CONFIG_PROC_FS
extern int __init nfs_fs_proc_init(void);
extern void nfs_fs_proc_exit(void);
#else
static inline int nfs_fs_proc_init(void)
{
return 0;
}
static inline void nfs_fs_proc_exit(void)
{
}
#endif
/* nfs4namespace.c */
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry); extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry);
#else #else
@ -46,6 +91,7 @@ extern void nfs_destroy_directcache(void);
#endif #endif
/* nfs2xdr.c */ /* nfs2xdr.c */
extern int nfs_stat_to_errno(int);
extern struct rpc_procinfo nfs_procedures[]; extern struct rpc_procinfo nfs_procedures[];
extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int); extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int);
@ -54,8 +100,9 @@ extern struct rpc_procinfo nfs3_procedures[];
extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int); extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int);
/* nfs4xdr.c */ /* nfs4xdr.c */
extern int nfs_stat_to_errno(int); #ifdef CONFIG_NFS_V4
extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
#endif
/* nfs4proc.c */ /* nfs4proc.c */
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
@ -66,6 +113,9 @@ extern int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry,
struct page *page); struct page *page);
#endif #endif
/* dir.c */
extern int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask);
/* inode.c */ /* inode.c */
extern struct inode *nfs_alloc_inode(struct super_block *sb); extern struct inode *nfs_alloc_inode(struct super_block *sb);
extern void nfs_destroy_inode(struct inode *); extern void nfs_destroy_inode(struct inode *);
@ -76,10 +126,10 @@ extern void nfs4_clear_inode(struct inode *);
#endif #endif
/* super.c */ /* super.c */
extern struct file_system_type nfs_referral_nfs4_fs_type; extern struct file_system_type nfs_xdev_fs_type;
extern struct file_system_type clone_nfs_fs_type;
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
extern struct file_system_type clone_nfs4_fs_type; extern struct file_system_type nfs4_xdev_fs_type;
extern struct file_system_type nfs4_referral_fs_type;
#endif #endif
extern struct rpc_stat nfs_rpcstat; extern struct rpc_stat nfs_rpcstat;
@ -88,30 +138,30 @@ extern int __init register_nfs_fs(void);
extern void __exit unregister_nfs_fs(void); extern void __exit unregister_nfs_fs(void);
/* namespace.c */ /* namespace.c */
extern char *nfs_path(const char *base, const struct dentry *dentry, extern char *nfs_path(const char *base,
const struct dentry *droot,
const struct dentry *dentry,
char *buffer, ssize_t buflen); char *buffer, ssize_t buflen);
/* /* getroot.c */
* Determine the mount path as a string extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *);
*/
static inline char *
nfs4_path(const struct dentry *dentry, char *buffer, ssize_t buflen)
{
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
return nfs_path(NFS_SB(dentry->d_sb)->mnt_path, dentry, buffer, buflen); extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *);
#else
return NULL; extern int nfs4_path_walk(struct nfs_server *server,
struct nfs_fh *mntfh,
const char *path);
#endif #endif
}
/* /*
* Determine the device name as a string * Determine the device name as a string
*/ */
static inline char *nfs_devname(const struct vfsmount *mnt_parent, static inline char *nfs_devname(const struct vfsmount *mnt_parent,
const struct dentry *dentry, const struct dentry *dentry,
char *buffer, ssize_t buflen) char *buffer, ssize_t buflen)
{ {
return nfs_path(mnt_parent->mnt_devname, dentry, buffer, buflen); return nfs_path(mnt_parent->mnt_devname, mnt_parent->mnt_root,
dentry, buffer, buflen);
} }
/* /*
@ -167,20 +217,3 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize)
if (sb->s_maxbytes > MAX_LFS_FILESIZE || sb->s_maxbytes <= 0) if (sb->s_maxbytes > MAX_LFS_FILESIZE || sb->s_maxbytes <= 0)
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
} }
/*
* Check if the string represents a "valid" IPv4 address
*/
static inline int valid_ipaddr4(const char *buf)
{
int rc, count, in[4];
rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]);
if (rc != 4)
return -EINVAL;
for (count = 0; count < 4; count++) {
if (in[count] > 255)
return -EINVAL;
}
return 0;
}

View File

@ -14,7 +14,6 @@
#include <linux/net.h> #include <linux/net.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xprt.h>
#include <linux/sunrpc/sched.h> #include <linux/sunrpc/sched.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
@ -77,22 +76,19 @@ static struct rpc_clnt *
mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version, mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version,
int protocol) int protocol)
{ {
struct rpc_xprt *xprt; struct rpc_create_args args = {
struct rpc_clnt *clnt; .protocol = protocol,
.address = (struct sockaddr *)srvaddr,
.addrsize = sizeof(*srvaddr),
.servername = hostname,
.program = &mnt_program,
.version = version,
.authflavor = RPC_AUTH_UNIX,
.flags = (RPC_CLNT_CREATE_ONESHOT |
RPC_CLNT_CREATE_INTR),
};
xprt = xprt_create_proto(protocol, srvaddr, NULL); return rpc_create(&args);
if (IS_ERR(xprt))
return (struct rpc_clnt *)xprt;
clnt = rpc_create_client(xprt, hostname,
&mnt_program, version,
RPC_AUTH_UNIX);
if (!IS_ERR(clnt)) {
clnt->cl_softrtry = 1;
clnt->cl_oneshot = 1;
clnt->cl_intr = 1;
}
return clnt;
} }
/* /*

View File

@ -2,6 +2,7 @@
* linux/fs/nfs/namespace.c * linux/fs/nfs/namespace.c
* *
* Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
* - Modified by David Howells <dhowells@redhat.com>
* *
* NFS namespace * NFS namespace
*/ */
@ -28,6 +29,7 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;
/* /*
* nfs_path - reconstruct the path given an arbitrary dentry * nfs_path - reconstruct the path given an arbitrary dentry
* @base - arbitrary string to prepend to the path * @base - arbitrary string to prepend to the path
* @droot - pointer to root dentry for mountpoint
* @dentry - pointer to dentry * @dentry - pointer to dentry
* @buffer - result buffer * @buffer - result buffer
* @buflen - length of buffer * @buflen - length of buffer
@ -38,7 +40,9 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;
* This is mainly for use in figuring out the path on the * This is mainly for use in figuring out the path on the
* server side when automounting on top of an existing partition. * server side when automounting on top of an existing partition.
*/ */
char *nfs_path(const char *base, const struct dentry *dentry, char *nfs_path(const char *base,
const struct dentry *droot,
const struct dentry *dentry,
char *buffer, ssize_t buflen) char *buffer, ssize_t buflen)
{ {
char *end = buffer+buflen; char *end = buffer+buflen;
@ -47,7 +51,7 @@ char *nfs_path(const char *base, const struct dentry *dentry,
*--end = '\0'; *--end = '\0';
buflen--; buflen--;
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
while (!IS_ROOT(dentry)) { while (!IS_ROOT(dentry) && dentry != droot) {
namelen = dentry->d_name.len; namelen = dentry->d_name.len;
buflen -= namelen + 1; buflen -= namelen + 1;
if (buflen < 0) if (buflen < 0)
@ -96,15 +100,18 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
struct nfs_fattr fattr; struct nfs_fattr fattr;
int err; int err;
dprintk("--> nfs_follow_mountpoint()\n");
BUG_ON(IS_ROOT(dentry)); BUG_ON(IS_ROOT(dentry));
dprintk("%s: enter\n", __FUNCTION__); dprintk("%s: enter\n", __FUNCTION__);
dput(nd->dentry); dput(nd->dentry);
nd->dentry = dget(dentry); nd->dentry = dget(dentry);
if (d_mountpoint(nd->dentry))
goto out_follow;
/* Look it up again */ /* Look it up again */
parent = dget_parent(nd->dentry); parent = dget_parent(nd->dentry);
err = server->rpc_ops->lookup(parent->d_inode, &nd->dentry->d_name, &fh, &fattr); err = server->nfs_client->rpc_ops->lookup(parent->d_inode,
&nd->dentry->d_name,
&fh, &fattr);
dput(parent); dput(parent);
if (err != 0) if (err != 0)
goto out_err; goto out_err;
@ -132,6 +139,8 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
out: out:
dprintk("%s: done, returned %d\n", __FUNCTION__, err); dprintk("%s: done, returned %d\n", __FUNCTION__, err);
dprintk("<-- nfs_follow_mountpoint() = %d\n", err);
return ERR_PTR(err); return ERR_PTR(err);
out_err: out_err:
path_release(nd); path_release(nd);
@ -172,22 +181,23 @@ void nfs_release_automount_timer(void)
/* /*
* Clone a mountpoint of the appropriate type * Clone a mountpoint of the appropriate type
*/ */
static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server, char *devname, static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
const char *devname,
struct nfs_clone_mount *mountdata) struct nfs_clone_mount *mountdata)
{ {
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
struct vfsmount *mnt = NULL; struct vfsmount *mnt = NULL;
switch (server->rpc_ops->version) { switch (server->nfs_client->cl_nfsversion) {
case 2: case 2:
case 3: case 3:
mnt = vfs_kern_mount(&clone_nfs_fs_type, 0, devname, mountdata); mnt = vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata);
break; break;
case 4: case 4:
mnt = vfs_kern_mount(&clone_nfs4_fs_type, 0, devname, mountdata); mnt = vfs_kern_mount(&nfs4_xdev_fs_type, 0, devname, mountdata);
} }
return mnt; return mnt;
#else #else
return vfs_kern_mount(&clone_nfs_fs_type, 0, devname, mountdata); return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata);
#endif #endif
} }
@ -213,6 +223,8 @@ struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent,
char *page = (char *) __get_free_page(GFP_USER); char *page = (char *) __get_free_page(GFP_USER);
char *devname; char *devname;
dprintk("--> nfs_do_submount()\n");
dprintk("%s: submounting on %s/%s\n", __FUNCTION__, dprintk("%s: submounting on %s/%s\n", __FUNCTION__,
dentry->d_parent->d_name.name, dentry->d_parent->d_name.name,
dentry->d_name.name); dentry->d_name.name);
@ -227,5 +239,7 @@ free_page:
free_page((unsigned long)page); free_page((unsigned long)page);
out: out:
dprintk("%s: done\n", __FUNCTION__); dprintk("%s: done\n", __FUNCTION__);
dprintk("<-- nfs_do_submount() = %p\n", mnt);
return mnt; return mnt;
} }

View File

@ -51,7 +51,7 @@
#define NFS_createargs_sz (NFS_diropargs_sz+NFS_sattr_sz) #define NFS_createargs_sz (NFS_diropargs_sz+NFS_sattr_sz)
#define NFS_renameargs_sz (NFS_diropargs_sz+NFS_diropargs_sz) #define NFS_renameargs_sz (NFS_diropargs_sz+NFS_diropargs_sz)
#define NFS_linkargs_sz (NFS_fhandle_sz+NFS_diropargs_sz) #define NFS_linkargs_sz (NFS_fhandle_sz+NFS_diropargs_sz)
#define NFS_symlinkargs_sz (NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz) #define NFS_symlinkargs_sz (NFS_diropargs_sz+1+NFS_sattr_sz)
#define NFS_readdirargs_sz (NFS_fhandle_sz+2) #define NFS_readdirargs_sz (NFS_fhandle_sz+2)
#define NFS_attrstat_sz (1+NFS_fattr_sz) #define NFS_attrstat_sz (1+NFS_fattr_sz)
@ -351,11 +351,26 @@ nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
static int static int
nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args) nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
{ {
struct xdr_buf *sndbuf = &req->rq_snd_buf;
size_t pad;
p = xdr_encode_fhandle(p, args->fromfh); p = xdr_encode_fhandle(p, args->fromfh);
p = xdr_encode_array(p, args->fromname, args->fromlen); p = xdr_encode_array(p, args->fromname, args->fromlen);
p = xdr_encode_array(p, args->topath, args->tolen); *p++ = htonl(args->pathlen);
sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
xdr_encode_pages(sndbuf, args->pages, 0, args->pathlen);
/*
* xdr_encode_pages may have added a few bytes to ensure the
* pathname ends on a 4-byte boundary. Start encoding the
* attributes after the pad bytes.
*/
pad = sndbuf->tail->iov_len;
if (pad > 0)
p++;
p = xdr_encode_sattr(p, args->sattr); p = xdr_encode_sattr(p, args->sattr);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); sndbuf->len += xdr_adjust_iovec(sndbuf->tail, p) - pad;
return 0; return 0;
} }

View File

@ -81,7 +81,7 @@ do_proc_get_root(struct rpc_clnt *client, struct nfs_fh *fhandle,
} }
/* /*
* Bare-bones access to getattr: this is for nfs_read_super. * Bare-bones access to getattr: this is for nfs_get_root/nfs_get_sb
*/ */
static int static int
nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
@ -90,8 +90,8 @@ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
int status; int status;
status = do_proc_get_root(server->client, fhandle, info); status = do_proc_get_root(server->client, fhandle, info);
if (status && server->client_sys != server->client) if (status && server->nfs_client->cl_rpcclient != server->client)
status = do_proc_get_root(server->client_sys, fhandle, info); status = do_proc_get_root(server->nfs_client->cl_rpcclient, fhandle, info);
return status; return status;
} }
@ -544,23 +544,23 @@ nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
} }
static int static int
nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
struct iattr *sattr, struct nfs_fh *fhandle, unsigned int len, struct iattr *sattr)
struct nfs_fattr *fattr)
{ {
struct nfs_fattr dir_attr; struct nfs_fh fhandle;
struct nfs_fattr fattr, dir_attr;
struct nfs3_symlinkargs arg = { struct nfs3_symlinkargs arg = {
.fromfh = NFS_FH(dir), .fromfh = NFS_FH(dir),
.fromname = name->name, .fromname = dentry->d_name.name,
.fromlen = name->len, .fromlen = dentry->d_name.len,
.topath = path->name, .pages = &page,
.tolen = path->len, .pathlen = len,
.sattr = sattr .sattr = sattr
}; };
struct nfs3_diropres res = { struct nfs3_diropres res = {
.dir_attr = &dir_attr, .dir_attr = &dir_attr,
.fh = fhandle, .fh = &fhandle,
.fattr = fattr .fattr = &fattr
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK], .rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK],
@ -569,13 +569,19 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
}; };
int status; int status;
if (path->len > NFS3_MAXPATHLEN) if (len > NFS3_MAXPATHLEN)
return -ENAMETOOLONG; return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
dprintk("NFS call symlink %s\n", dentry->d_name.name);
nfs_fattr_init(&dir_attr); nfs_fattr_init(&dir_attr);
nfs_fattr_init(fattr); nfs_fattr_init(&fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_post_op_update_inode(dir, &dir_attr); nfs_post_op_update_inode(dir, &dir_attr);
if (status != 0)
goto out;
status = nfs_instantiate(dentry, &fhandle, &fattr);
out:
dprintk("NFS reply symlink: %d\n", status); dprintk("NFS reply symlink: %d\n", status);
return status; return status;
} }
@ -785,7 +791,7 @@ nfs3_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
dprintk("NFS call fsinfo\n"); dprintk("NFS call fsinfo\n");
nfs_fattr_init(info->fattr); nfs_fattr_init(info->fattr);
status = rpc_call_sync(server->client_sys, &msg, 0); status = rpc_call_sync(server->nfs_client->cl_rpcclient, &msg, 0);
dprintk("NFS reply fsinfo: %d\n", status); dprintk("NFS reply fsinfo: %d\n", status);
return status; return status;
} }
@ -886,7 +892,7 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
return nlmclnt_proc(filp->f_dentry->d_inode, cmd, fl); return nlmclnt_proc(filp->f_dentry->d_inode, cmd, fl);
} }
struct nfs_rpc_ops nfs_v3_clientops = { const struct nfs_rpc_ops nfs_v3_clientops = {
.version = 3, /* protocol version */ .version = 3, /* protocol version */
.dentry_ops = &nfs_dentry_operations, .dentry_ops = &nfs_dentry_operations,
.dir_inode_ops = &nfs3_dir_inode_operations, .dir_inode_ops = &nfs3_dir_inode_operations,

View File

@ -56,7 +56,7 @@
#define NFS3_writeargs_sz (NFS3_fh_sz+5) #define NFS3_writeargs_sz (NFS3_fh_sz+5)
#define NFS3_createargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz) #define NFS3_createargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
#define NFS3_mkdirargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz) #define NFS3_mkdirargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
#define NFS3_symlinkargs_sz (NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz) #define NFS3_symlinkargs_sz (NFS3_diropargs_sz+1+NFS3_sattr_sz)
#define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz) #define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz)
#define NFS3_renameargs_sz (NFS3_diropargs_sz+NFS3_diropargs_sz) #define NFS3_renameargs_sz (NFS3_diropargs_sz+NFS3_diropargs_sz)
#define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz) #define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz)
@ -398,8 +398,11 @@ nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args
p = xdr_encode_fhandle(p, args->fromfh); p = xdr_encode_fhandle(p, args->fromfh);
p = xdr_encode_array(p, args->fromname, args->fromlen); p = xdr_encode_array(p, args->fromname, args->fromlen);
p = xdr_encode_sattr(p, args->sattr); p = xdr_encode_sattr(p, args->sattr);
p = xdr_encode_array(p, args->topath, args->tolen); *p++ = htonl(args->pathlen);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
/* Copy the page */
xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
return 0; return 0;
} }

View File

@ -42,55 +42,6 @@ enum nfs4_client_state {
NFS4CLNT_LEASE_EXPIRED, NFS4CLNT_LEASE_EXPIRED,
}; };
/*
* The nfs4_client identifies our client state to the server.
*/
struct nfs4_client {
struct list_head cl_servers; /* Global list of servers */
struct in_addr cl_addr; /* Server identifier */
u64 cl_clientid; /* constant */
nfs4_verifier cl_confirm;
unsigned long cl_state;
u32 cl_lockowner_id;
/*
* The following rwsem ensures exclusive access to the server
* while we recover the state following a lease expiration.
*/
struct rw_semaphore cl_sem;
struct list_head cl_delegations;
struct list_head cl_state_owners;
struct list_head cl_unused;
int cl_nunused;
spinlock_t cl_lock;
atomic_t cl_count;
struct rpc_clnt * cl_rpcclient;
struct list_head cl_superblocks; /* List of nfs_server structs */
unsigned long cl_lease_time;
unsigned long cl_last_renewal;
struct work_struct cl_renewd;
struct work_struct cl_recoverd;
struct rpc_wait_queue cl_rpcwaitq;
/* used for the setclientid verifier */
struct timespec cl_boot_time;
/* idmapper */
struct idmap * cl_idmap;
/* Our own IP address, as a null-terminated string.
* This is used to generate the clientid, and the callback address.
*/
char cl_ipaddr[16];
unsigned char cl_id_uniquifier;
};
/* /*
* struct rpc_sequence ensures that RPC calls are sent in the exact * struct rpc_sequence ensures that RPC calls are sent in the exact
* order that they appear on the list. * order that they appear on the list.
@ -127,7 +78,7 @@ static inline void nfs_confirm_seqid(struct nfs_seqid_counter *seqid, int status
struct nfs4_state_owner { struct nfs4_state_owner {
spinlock_t so_lock; spinlock_t so_lock;
struct list_head so_list; /* per-clientid list of state_owners */ struct list_head so_list; /* per-clientid list of state_owners */
struct nfs4_client *so_client; struct nfs_client *so_client;
u32 so_id; /* 32-bit identifier, unique */ u32 so_id; /* 32-bit identifier, unique */
atomic_t so_count; atomic_t so_count;
@ -210,10 +161,10 @@ extern ssize_t nfs4_listxattr(struct dentry *, char *, size_t);
/* nfs4proc.c */ /* nfs4proc.c */
extern int nfs4_map_errors(int err); extern int nfs4_map_errors(int err);
extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short, struct rpc_cred *); extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *);
extern int nfs4_proc_setclientid_confirm(struct nfs4_client *, struct rpc_cred *); extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *);
extern int nfs4_proc_async_renew(struct nfs4_client *, struct rpc_cred *); extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *);
extern int nfs4_proc_renew(struct nfs4_client *, struct rpc_cred *); extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *);
extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state); extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state);
extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);
@ -231,19 +182,14 @@ extern const u32 nfs4_fsinfo_bitmap[2];
extern const u32 nfs4_fs_locations_bitmap[2]; extern const u32 nfs4_fs_locations_bitmap[2];
/* nfs4renewd.c */ /* nfs4renewd.c */
extern void nfs4_schedule_state_renewal(struct nfs4_client *); extern void nfs4_schedule_state_renewal(struct nfs_client *);
extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); extern void nfs4_renewd_prepare_shutdown(struct nfs_server *);
extern void nfs4_kill_renewd(struct nfs4_client *); extern void nfs4_kill_renewd(struct nfs_client *);
extern void nfs4_renew_state(void *); extern void nfs4_renew_state(void *);
/* nfs4state.c */ /* nfs4state.c */
extern void init_nfsv4_state(struct nfs_server *); struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp);
extern void destroy_nfsv4_state(struct nfs_server *); extern u32 nfs4_alloc_lockowner_id(struct nfs_client *);
extern struct nfs4_client *nfs4_get_client(struct in_addr *);
extern void nfs4_put_client(struct nfs4_client *clp);
extern struct nfs4_client *nfs4_find_client(struct in_addr *);
struct rpc_cred *nfs4_get_renew_cred(struct nfs4_client *clp);
extern u32 nfs4_alloc_lockowner_id(struct nfs4_client *);
extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern void nfs4_put_state_owner(struct nfs4_state_owner *);
@ -252,7 +198,7 @@ extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state
extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_put_open_state(struct nfs4_state *);
extern void nfs4_close_state(struct nfs4_state *, mode_t); extern void nfs4_close_state(struct nfs4_state *, mode_t);
extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t);
extern void nfs4_schedule_state_recovery(struct nfs4_client *); extern void nfs4_schedule_state_recovery(struct nfs_client *);
extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl); extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t); extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);
@ -276,10 +222,6 @@ extern struct svc_version nfs4_callback_version1;
#else #else
#define init_nfsv4_state(server) do { } while (0)
#define destroy_nfsv4_state(server) do { } while (0)
#define nfs4_put_state_owner(inode, owner) do { } while (0)
#define nfs4_put_open_state(state) do { } while (0)
#define nfs4_close_state(a, b) do { } while (0) #define nfs4_close_state(a, b) do { } while (0)
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */

View File

@ -2,6 +2,7 @@
* linux/fs/nfs/nfs4namespace.c * linux/fs/nfs/nfs4namespace.c
* *
* Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
* - Modified by David Howells <dhowells@redhat.com>
* *
* NFSv4 namespace * NFSv4 namespace
*/ */
@ -23,7 +24,7 @@
/* /*
* Check if fs_root is valid * Check if fs_root is valid
*/ */
static inline char *nfs4_pathname_string(struct nfs4_pathname *pathname, static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
char *buffer, ssize_t buflen) char *buffer, ssize_t buflen)
{ {
char *end = buffer + buflen; char *end = buffer + buflen;
@ -34,7 +35,7 @@ static inline char *nfs4_pathname_string(struct nfs4_pathname *pathname,
n = pathname->ncomponents; n = pathname->ncomponents;
while (--n >= 0) { while (--n >= 0) {
struct nfs4_string *component = &pathname->components[n]; const struct nfs4_string *component = &pathname->components[n];
buflen -= component->len + 1; buflen -= component->len + 1;
if (buflen < 0) if (buflen < 0)
goto Elong; goto Elong;
@ -47,6 +48,68 @@ Elong:
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
} }
/*
* Determine the mount path as a string
*/
static char *nfs4_path(const struct vfsmount *mnt_parent,
const struct dentry *dentry,
char *buffer, ssize_t buflen)
{
const char *srvpath;
srvpath = strchr(mnt_parent->mnt_devname, ':');
if (srvpath)
srvpath++;
else
srvpath = mnt_parent->mnt_devname;
return nfs_path(srvpath, mnt_parent->mnt_root, dentry, buffer, buflen);
}
/*
* Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
* believe to be the server path to this dentry
*/
static int nfs4_validate_fspath(const struct vfsmount *mnt_parent,
const struct dentry *dentry,
const struct nfs4_fs_locations *locations,
char *page, char *page2)
{
const char *path, *fs_path;
path = nfs4_path(mnt_parent, dentry, page, PAGE_SIZE);
if (IS_ERR(path))
return PTR_ERR(path);
fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE);
if (IS_ERR(fs_path))
return PTR_ERR(fs_path);
if (strncmp(path, fs_path, strlen(fs_path)) != 0) {
dprintk("%s: path %s does not begin with fsroot %s\n",
__FUNCTION__, path, fs_path);
return -ENOENT;
}
return 0;
}
/*
* Check if the string represents a "valid" IPv4 address
*/
static inline int valid_ipaddr4(const char *buf)
{
int rc, count, in[4];
rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]);
if (rc != 4)
return -EINVAL;
for (count = 0; count < 4; count++) {
if (in[count] > 255)
return -EINVAL;
}
return 0;
}
/** /**
* nfs_follow_referral - set up mountpoint when hitting a referral on moved error * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
@ -60,7 +123,7 @@ Elong:
*/ */
static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
const struct dentry *dentry, const struct dentry *dentry,
struct nfs4_fs_locations *locations) const struct nfs4_fs_locations *locations)
{ {
struct vfsmount *mnt = ERR_PTR(-ENOENT); struct vfsmount *mnt = ERR_PTR(-ENOENT);
struct nfs_clone_mount mountdata = { struct nfs_clone_mount mountdata = {
@ -68,10 +131,9 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
.dentry = dentry, .dentry = dentry,
.authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor, .authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor,
}; };
char *page, *page2; char *page = NULL, *page2 = NULL;
char *path, *fs_path;
char *devname; char *devname;
int loc, s; int loc, s, error;
if (locations == NULL || locations->nlocations <= 0) if (locations == NULL || locations->nlocations <= 0)
goto out; goto out;
@ -79,36 +141,30 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
dprintk("%s: referral at %s/%s\n", __FUNCTION__, dprintk("%s: referral at %s/%s\n", __FUNCTION__,
dentry->d_parent->d_name.name, dentry->d_name.name); dentry->d_parent->d_name.name, dentry->d_name.name);
/* Ensure fs path is a prefix of current dentry path */
page = (char *) __get_free_page(GFP_USER); page = (char *) __get_free_page(GFP_USER);
if (page == NULL) if (!page)
goto out; goto out;
page2 = (char *) __get_free_page(GFP_USER); page2 = (char *) __get_free_page(GFP_USER);
if (page2 == NULL) if (!page2)
goto out; goto out;
path = nfs4_path(dentry, page, PAGE_SIZE); /* Ensure fs path is a prefix of current dentry path */
if (IS_ERR(path)) error = nfs4_validate_fspath(mnt_parent, dentry, locations, page, page2);
goto out_free; if (error < 0) {
mnt = ERR_PTR(error);
fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE); goto out;
if (IS_ERR(fs_path))
goto out_free;
if (strncmp(path, fs_path, strlen(fs_path)) != 0) {
dprintk("%s: path %s does not begin with fsroot %s\n", __FUNCTION__, path, fs_path);
goto out_free;
} }
devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE);
if (IS_ERR(devname)) { if (IS_ERR(devname)) {
mnt = (struct vfsmount *)devname; mnt = (struct vfsmount *)devname;
goto out_free; goto out;
} }
loc = 0; loc = 0;
while (loc < locations->nlocations && IS_ERR(mnt)) { while (loc < locations->nlocations && IS_ERR(mnt)) {
struct nfs4_fs_location *location = &locations->locations[loc]; const struct nfs4_fs_location *location = &locations->locations[loc];
char *mnt_path; char *mnt_path;
if (location == NULL || location->nservers <= 0 || if (location == NULL || location->nservers <= 0 ||
@ -140,7 +196,7 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
addr.sin_port = htons(NFS_PORT); addr.sin_port = htons(NFS_PORT);
mountdata.addr = &addr; mountdata.addr = &addr;
mnt = vfs_kern_mount(&nfs_referral_nfs4_fs_type, 0, devname, &mountdata); mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, devname, &mountdata);
if (!IS_ERR(mnt)) { if (!IS_ERR(mnt)) {
break; break;
} }
@ -149,10 +205,9 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
loc++; loc++;
} }
out_free:
free_page((unsigned long)page);
free_page((unsigned long)page2);
out: out:
free_page((unsigned long) page);
free_page((unsigned long) page2);
dprintk("%s: done\n", __FUNCTION__); dprintk("%s: done\n", __FUNCTION__);
return mnt; return mnt;
} }
@ -165,7 +220,7 @@ out:
*/ */
struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry) struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry)
{ {
struct vfsmount *mnt = ERR_PTR(-ENOENT); struct vfsmount *mnt = ERR_PTR(-ENOMEM);
struct dentry *parent; struct dentry *parent;
struct nfs4_fs_locations *fs_locations = NULL; struct nfs4_fs_locations *fs_locations = NULL;
struct page *page; struct page *page;
@ -183,11 +238,16 @@ struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentr
goto out_free; goto out_free;
/* Get locations */ /* Get locations */
mnt = ERR_PTR(-ENOENT);
parent = dget_parent(dentry); parent = dget_parent(dentry);
dprintk("%s: getting locations for %s/%s\n", __FUNCTION__, parent->d_name.name, dentry->d_name.name); dprintk("%s: getting locations for %s/%s\n",
__FUNCTION__, parent->d_name.name, dentry->d_name.name);
err = nfs4_proc_fs_locations(parent->d_inode, dentry, fs_locations, page); err = nfs4_proc_fs_locations(parent->d_inode, dentry, fs_locations, page);
dput(parent); dput(parent);
if (err != 0 || fs_locations->nlocations <= 0 || if (err != 0 ||
fs_locations->nlocations <= 0 ||
fs_locations->fs_path.ncomponents <= 0) fs_locations->fs_path.ncomponents <= 0)
goto out_free; goto out_free;

View File

@ -55,7 +55,7 @@
#define NFSDBG_FACILITY NFSDBG_PROC #define NFSDBG_FACILITY NFSDBG_PROC
#define NFS4_POLL_RETRY_MIN (1*HZ) #define NFS4_POLL_RETRY_MIN (HZ/10)
#define NFS4_POLL_RETRY_MAX (15*HZ) #define NFS4_POLL_RETRY_MAX (15*HZ)
struct nfs4_opendata; struct nfs4_opendata;
@ -64,7 +64,7 @@ static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinf
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *); static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *);
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry); static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception); static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp); static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp);
/* Prevent leaks of NFSv4 errors into userland */ /* Prevent leaks of NFSv4 errors into userland */
int nfs4_map_errors(int err) int nfs4_map_errors(int err)
@ -195,7 +195,7 @@ static void nfs4_setup_readdir(u64 cookie, u32 *verifier, struct dentry *dentry,
static void renew_lease(const struct nfs_server *server, unsigned long timestamp) static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
{ {
struct nfs4_client *clp = server->nfs4_state; struct nfs_client *clp = server->nfs_client;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
if (time_before(clp->cl_last_renewal,timestamp)) if (time_before(clp->cl_last_renewal,timestamp))
clp->cl_last_renewal = timestamp; clp->cl_last_renewal = timestamp;
@ -252,7 +252,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
atomic_inc(&sp->so_count); atomic_inc(&sp->so_count);
p->o_arg.fh = NFS_FH(dir); p->o_arg.fh = NFS_FH(dir);
p->o_arg.open_flags = flags, p->o_arg.open_flags = flags,
p->o_arg.clientid = server->nfs4_state->cl_clientid; p->o_arg.clientid = server->nfs_client->cl_clientid;
p->o_arg.id = sp->so_id; p->o_arg.id = sp->so_id;
p->o_arg.name = &dentry->d_name; p->o_arg.name = &dentry->d_name;
p->o_arg.server = server; p->o_arg.server = server;
@ -550,7 +550,7 @@ int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state)
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
/* Don't recall a delegation if it was lost */ /* Don't recall a delegation if it was lost */
nfs4_schedule_state_recovery(server->nfs4_state); nfs4_schedule_state_recovery(server->nfs_client);
return err; return err;
} }
err = nfs4_handle_exception(server, err, &exception); err = nfs4_handle_exception(server, err, &exception);
@ -758,7 +758,7 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
} }
nfs_confirm_seqid(&data->owner->so_seqid, 0); nfs_confirm_seqid(&data->owner->so_seqid, 0);
if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
return server->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr); return server->nfs_client->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr);
return 0; return 0;
} }
@ -792,11 +792,18 @@ out:
int nfs4_recover_expired_lease(struct nfs_server *server) int nfs4_recover_expired_lease(struct nfs_server *server)
{ {
struct nfs4_client *clp = server->nfs4_state; struct nfs_client *clp = server->nfs_client;
int ret;
if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) for (;;) {
ret = nfs4_wait_clnt_recover(server->client, clp);
if (ret != 0)
return ret;
if (!test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
break;
nfs4_schedule_state_recovery(clp); nfs4_schedule_state_recovery(clp);
return nfs4_wait_clnt_recover(server->client, clp); }
return 0;
} }
/* /*
@ -867,7 +874,7 @@ static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_client *clp = server->nfs4_state; struct nfs_client *clp = server->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs4_state_owner *sp = NULL; struct nfs4_state_owner *sp = NULL;
struct nfs4_state *state = NULL; struct nfs4_state *state = NULL;
@ -953,7 +960,7 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct nfs4_state *state = NULL; struct nfs4_state *state = NULL;
struct nfs_server *server = NFS_SERVER(dir); struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_client *clp = server->nfs4_state; struct nfs_client *clp = server->nfs_client;
struct nfs4_opendata *opendata; struct nfs4_opendata *opendata;
int status; int status;
@ -1133,7 +1140,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
break; break;
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
nfs4_schedule_state_recovery(server->nfs4_state); nfs4_schedule_state_recovery(server->nfs_client);
break; break;
default: default:
if (nfs4_async_handle_error(task, server) == -EAGAIN) { if (nfs4_async_handle_error(task, server) == -EAGAIN) {
@ -1268,7 +1275,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
BUG_ON(nd->intent.open.flags & O_CREAT); BUG_ON(nd->intent.open.flags & O_CREAT);
} }
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
if (IS_ERR(cred)) if (IS_ERR(cred))
return (struct dentry *)cred; return (struct dentry *)cred;
state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred); state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred);
@ -1291,7 +1298,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
struct rpc_cred *cred; struct rpc_cred *cred;
struct nfs4_state *state; struct nfs4_state *state;
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
if (IS_ERR(cred)) if (IS_ERR(cred))
return PTR_ERR(cred); return PTR_ERR(cred);
state = nfs4_open_delegated(dentry->d_inode, openflags, cred); state = nfs4_open_delegated(dentry->d_inode, openflags, cred);
@ -1393,70 +1400,19 @@ static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
return err; return err;
} }
/*
* get the file handle for the "/" directory on the server
*/
static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info) struct nfs_fsinfo *info)
{ {
struct nfs_fattr * fattr = info->fattr;
unsigned char * p;
struct qstr q;
struct nfs4_lookup_arg args = {
.dir_fh = fhandle,
.name = &q,
.bitmask = nfs4_fattr_bitmap,
};
struct nfs4_lookup_res res = {
.server = server,
.fattr = fattr,
.fh = fhandle,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP],
.rpc_argp = &args,
.rpc_resp = &res,
};
int status; int status;
/*
* Now we do a separate LOOKUP for each component of the mount path.
* The LOOKUPs are done separately so that we can conveniently
* catch an ERR_WRONGSEC if it occurs along the way...
*/
status = nfs4_lookup_root(server, fhandle, info); status = nfs4_lookup_root(server, fhandle, info);
if (status)
goto out;
p = server->mnt_path;
for (;;) {
struct nfs4_exception exception = { };
while (*p == '/')
p++;
if (!*p)
break;
q.name = p;
while (*p && (*p != '/'))
p++;
q.len = p - q.name;
do {
nfs_fattr_init(fattr);
status = nfs4_handle_exception(server,
rpc_call_sync(server->client, &msg, 0),
&exception);
} while (exception.retry);
if (status == 0)
continue;
if (status == -ENOENT) {
printk(KERN_NOTICE "NFS: mount path %s does not exist!\n", server->mnt_path);
printk(KERN_NOTICE "NFS: suggestion: try mounting '/' instead.\n");
}
break;
}
if (status == 0) if (status == 0)
status = nfs4_server_capabilities(server, fhandle); status = nfs4_server_capabilities(server, fhandle);
if (status == 0) if (status == 0)
status = nfs4_do_fsinfo(server, fhandle, info); status = nfs4_do_fsinfo(server, fhandle, info);
out:
return nfs4_map_errors(status); return nfs4_map_errors(status);
} }
@ -1565,7 +1521,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
nfs_fattr_init(fattr); nfs_fattr_init(fattr);
cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
if (IS_ERR(cred)) if (IS_ERR(cred))
return PTR_ERR(cred); return PTR_ERR(cred);
@ -1583,6 +1539,52 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
return status; return status;
} }
static int _nfs4_proc_lookupfh(struct nfs_server *server, struct nfs_fh *dirfh,
struct qstr *name, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
int status;
struct nfs4_lookup_arg args = {
.bitmask = server->attr_bitmask,
.dir_fh = dirfh,
.name = name,
};
struct nfs4_lookup_res res = {
.server = server,
.fattr = fattr,
.fh = fhandle,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP],
.rpc_argp = &args,
.rpc_resp = &res,
};
nfs_fattr_init(fattr);
dprintk("NFS call lookupfh %s\n", name->name);
status = rpc_call_sync(server->client, &msg, 0);
dprintk("NFS reply lookupfh: %d\n", status);
if (status == -NFS4ERR_MOVED)
status = -EREMOTE;
return status;
}
static int nfs4_proc_lookupfh(struct nfs_server *server, struct nfs_fh *dirfh,
struct qstr *name, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(server,
_nfs4_proc_lookupfh(server, dirfh, name,
fhandle, fattr),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name, static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr) struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{ {
@ -1881,7 +1883,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
struct rpc_cred *cred; struct rpc_cred *cred;
int status = 0; int status = 0;
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
if (IS_ERR(cred)) { if (IS_ERR(cred)) {
status = PTR_ERR(cred); status = PTR_ERR(cred);
goto out; goto out;
@ -2089,24 +2091,24 @@ static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *n
return err; return err;
} }
static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name, static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle, struct page *page, unsigned int len, struct iattr *sattr)
struct nfs_fattr *fattr)
{ {
struct nfs_server *server = NFS_SERVER(dir); struct nfs_server *server = NFS_SERVER(dir);
struct nfs_fattr dir_fattr; struct nfs_fh fhandle;
struct nfs_fattr fattr, dir_fattr;
struct nfs4_create_arg arg = { struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir), .dir_fh = NFS_FH(dir),
.server = server, .server = server,
.name = name, .name = &dentry->d_name,
.attrs = sattr, .attrs = sattr,
.ftype = NF4LNK, .ftype = NF4LNK,
.bitmask = server->attr_bitmask, .bitmask = server->attr_bitmask,
}; };
struct nfs4_create_res res = { struct nfs4_create_res res = {
.server = server, .server = server,
.fh = fhandle, .fh = &fhandle,
.fattr = fattr, .fattr = &fattr,
.dir_fattr = &dir_fattr, .dir_fattr = &dir_fattr,
}; };
struct rpc_message msg = { struct rpc_message msg = {
@ -2116,29 +2118,32 @@ static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name,
}; };
int status; int status;
if (path->len > NFS4_MAXPATHLEN) if (len > NFS4_MAXPATHLEN)
return -ENAMETOOLONG; return -ENAMETOOLONG;
arg.u.symlink = path;
nfs_fattr_init(fattr); arg.u.symlink.pages = &page;
arg.u.symlink.len = len;
nfs_fattr_init(&fattr);
nfs_fattr_init(&dir_fattr); nfs_fattr_init(&dir_fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (!status) if (!status) {
update_changeattr(dir, &res.dir_cinfo); update_changeattr(dir, &res.dir_cinfo);
nfs_post_op_update_inode(dir, res.dir_fattr); nfs_post_op_update_inode(dir, res.dir_fattr);
status = nfs_instantiate(dentry, &fhandle, &fattr);
}
return status; return status;
} }
static int nfs4_proc_symlink(struct inode *dir, struct qstr *name, static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle, struct page *page, unsigned int len, struct iattr *sattr)
struct nfs_fattr *fattr)
{ {
struct nfs4_exception exception = { }; struct nfs4_exception exception = { };
int err; int err;
do { do {
err = nfs4_handle_exception(NFS_SERVER(dir), err = nfs4_handle_exception(NFS_SERVER(dir),
_nfs4_proc_symlink(dir, name, path, sattr, _nfs4_proc_symlink(dir, dentry, page,
fhandle, fattr), len, sattr),
&exception); &exception);
} while (exception.retry); } while (exception.retry);
return err; return err;
@ -2521,7 +2526,7 @@ static void nfs4_proc_commit_setup(struct nfs_write_data *data, int how)
*/ */
static void nfs4_renew_done(struct rpc_task *task, void *data) static void nfs4_renew_done(struct rpc_task *task, void *data)
{ {
struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp; struct nfs_client *clp = (struct nfs_client *)task->tk_msg.rpc_argp;
unsigned long timestamp = (unsigned long)data; unsigned long timestamp = (unsigned long)data;
if (task->tk_status < 0) { if (task->tk_status < 0) {
@ -2543,7 +2548,7 @@ static const struct rpc_call_ops nfs4_renew_ops = {
.rpc_call_done = nfs4_renew_done, .rpc_call_done = nfs4_renew_done,
}; };
int nfs4_proc_async_renew(struct nfs4_client *clp, struct rpc_cred *cred) int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred)
{ {
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
@ -2555,7 +2560,7 @@ int nfs4_proc_async_renew(struct nfs4_client *clp, struct rpc_cred *cred)
&nfs4_renew_ops, (void *)jiffies); &nfs4_renew_ops, (void *)jiffies);
} }
int nfs4_proc_renew(struct nfs4_client *clp, struct rpc_cred *cred) int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)
{ {
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
@ -2770,7 +2775,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
return -EOPNOTSUPP; return -EOPNOTSUPP;
nfs_inode_return_delegation(inode); nfs_inode_return_delegation(inode);
buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase); buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
ret = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0); ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (ret == 0) if (ret == 0)
nfs4_write_cached_acl(inode, buf, buflen); nfs4_write_cached_acl(inode, buf, buflen);
return ret; return ret;
@ -2791,7 +2796,7 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
static int static int
nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server) nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
{ {
struct nfs4_client *clp = server->nfs4_state; struct nfs_client *clp = server->nfs_client;
if (!clp || task->tk_status >= 0) if (!clp || task->tk_status >= 0)
return 0; return 0;
@ -2828,7 +2833,7 @@ static int nfs4_wait_bit_interruptible(void *word)
return 0; return 0;
} }
static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp) static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp)
{ {
sigset_t oldset; sigset_t oldset;
int res; int res;
@ -2871,7 +2876,7 @@ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
*/ */
int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception) int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
{ {
struct nfs4_client *clp = server->nfs4_state; struct nfs_client *clp = server->nfs_client;
int ret = errorcode; int ret = errorcode;
exception->retry = 0; exception->retry = 0;
@ -2886,6 +2891,7 @@ int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct
if (ret == 0) if (ret == 0)
exception->retry = 1; exception->retry = 1;
break; break;
case -NFS4ERR_FILE_OPEN:
case -NFS4ERR_GRACE: case -NFS4ERR_GRACE:
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
ret = nfs4_delay(server->client, &exception->timeout); ret = nfs4_delay(server->client, &exception->timeout);
@ -2898,7 +2904,7 @@ int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct
return nfs4_map_errors(ret); return nfs4_map_errors(ret);
} }
int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port, struct rpc_cred *cred) int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
{ {
nfs4_verifier sc_verifier; nfs4_verifier sc_verifier;
struct nfs4_setclientid setclientid = { struct nfs4_setclientid setclientid = {
@ -2922,7 +2928,7 @@ int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short p
for(;;) { for(;;) {
setclientid.sc_name_len = scnprintf(setclientid.sc_name, setclientid.sc_name_len = scnprintf(setclientid.sc_name,
sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u %s %u", sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u %s %u",
clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr), clp->cl_ipaddr, NIPQUAD(clp->cl_addr.sin_addr),
cred->cr_ops->cr_name, cred->cr_ops->cr_name,
clp->cl_id_uniquifier); clp->cl_id_uniquifier);
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid, setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
@ -2945,7 +2951,7 @@ int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short p
return status; return status;
} }
static int _nfs4_proc_setclientid_confirm(struct nfs4_client *clp, struct rpc_cred *cred) static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred)
{ {
struct nfs_fsinfo fsinfo; struct nfs_fsinfo fsinfo;
struct rpc_message msg = { struct rpc_message msg = {
@ -2969,7 +2975,7 @@ static int _nfs4_proc_setclientid_confirm(struct nfs4_client *clp, struct rpc_cr
return status; return status;
} }
int nfs4_proc_setclientid_confirm(struct nfs4_client *clp, struct rpc_cred *cred) int nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred)
{ {
long timeout; long timeout;
int err; int err;
@ -3077,7 +3083,7 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4
switch (err) { switch (err) {
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
nfs4_schedule_state_recovery(server->nfs4_state); nfs4_schedule_state_recovery(server->nfs_client);
case 0: case 0:
return 0; return 0;
} }
@ -3106,7 +3112,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
{ {
struct inode *inode = state->inode; struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_client *clp = server->nfs4_state; struct nfs_client *clp = server->nfs_client;
struct nfs_lockt_args arg = { struct nfs_lockt_args arg = {
.fh = NFS_FH(inode), .fh = NFS_FH(inode),
.fl = request, .fl = request,
@ -3231,7 +3237,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
break; break;
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
nfs4_schedule_state_recovery(calldata->server->nfs4_state); nfs4_schedule_state_recovery(calldata->server->nfs_client);
break; break;
default: default:
if (nfs4_async_handle_error(task, calldata->server) == -EAGAIN) { if (nfs4_async_handle_error(task, calldata->server) == -EAGAIN) {
@ -3343,7 +3349,7 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
if (p->arg.lock_seqid == NULL) if (p->arg.lock_seqid == NULL)
goto out_free; goto out_free;
p->arg.lock_stateid = &lsp->ls_stateid; p->arg.lock_stateid = &lsp->ls_stateid;
p->arg.lock_owner.clientid = server->nfs4_state->cl_clientid; p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
p->arg.lock_owner.id = lsp->ls_id; p->arg.lock_owner.id = lsp->ls_id;
p->lsp = lsp; p->lsp = lsp;
atomic_inc(&lsp->ls_count); atomic_inc(&lsp->ls_count);
@ -3513,7 +3519,7 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{ {
struct nfs4_client *clp = state->owner->so_client; struct nfs_client *clp = state->owner->so_client;
unsigned char fl_flags = request->fl_flags; unsigned char fl_flags = request->fl_flags;
int status; int status;
@ -3715,7 +3721,7 @@ static struct inode_operations nfs4_file_inode_operations = {
.listxattr = nfs4_listxattr, .listxattr = nfs4_listxattr,
}; };
struct nfs_rpc_ops nfs_v4_clientops = { const struct nfs_rpc_ops nfs_v4_clientops = {
.version = 4, /* protocol version */ .version = 4, /* protocol version */
.dentry_ops = &nfs4_dentry_operations, .dentry_ops = &nfs4_dentry_operations,
.dir_inode_ops = &nfs4_dir_inode_operations, .dir_inode_ops = &nfs4_dir_inode_operations,
@ -3723,6 +3729,7 @@ struct nfs_rpc_ops nfs_v4_clientops = {
.getroot = nfs4_proc_get_root, .getroot = nfs4_proc_get_root,
.getattr = nfs4_proc_getattr, .getattr = nfs4_proc_getattr,
.setattr = nfs4_proc_setattr, .setattr = nfs4_proc_setattr,
.lookupfh = nfs4_proc_lookupfh,
.lookup = nfs4_proc_lookup, .lookup = nfs4_proc_lookup,
.access = nfs4_proc_access, .access = nfs4_proc_access,
.readlink = nfs4_proc_readlink, .readlink = nfs4_proc_readlink,
@ -3743,6 +3750,7 @@ struct nfs_rpc_ops nfs_v4_clientops = {
.statfs = nfs4_proc_statfs, .statfs = nfs4_proc_statfs,
.fsinfo = nfs4_proc_fsinfo, .fsinfo = nfs4_proc_fsinfo,
.pathconf = nfs4_proc_pathconf, .pathconf = nfs4_proc_pathconf,
.set_capabilities = nfs4_server_capabilities,
.decode_dirent = nfs4_decode_dirent, .decode_dirent = nfs4_decode_dirent,
.read_setup = nfs4_proc_read_setup, .read_setup = nfs4_proc_read_setup,
.read_done = nfs4_read_done, .read_done = nfs4_read_done,

View File

@ -61,7 +61,7 @@
void void
nfs4_renew_state(void *data) nfs4_renew_state(void *data)
{ {
struct nfs4_client *clp = (struct nfs4_client *)data; struct nfs_client *clp = (struct nfs_client *)data;
struct rpc_cred *cred; struct rpc_cred *cred;
long lease, timeout; long lease, timeout;
unsigned long last, now; unsigned long last, now;
@ -108,7 +108,7 @@ out:
/* Must be called with clp->cl_sem locked for writes */ /* Must be called with clp->cl_sem locked for writes */
void void
nfs4_schedule_state_renewal(struct nfs4_client *clp) nfs4_schedule_state_renewal(struct nfs_client *clp)
{ {
long timeout; long timeout;
@ -121,32 +121,20 @@ nfs4_schedule_state_renewal(struct nfs4_client *clp)
__FUNCTION__, (timeout + HZ - 1) / HZ); __FUNCTION__, (timeout + HZ - 1) / HZ);
cancel_delayed_work(&clp->cl_renewd); cancel_delayed_work(&clp->cl_renewd);
schedule_delayed_work(&clp->cl_renewd, timeout); schedule_delayed_work(&clp->cl_renewd, timeout);
set_bit(NFS_CS_RENEWD, &clp->cl_res_state);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
} }
void void
nfs4_renewd_prepare_shutdown(struct nfs_server *server) nfs4_renewd_prepare_shutdown(struct nfs_server *server)
{ {
struct nfs4_client *clp = server->nfs4_state;
if (!clp)
return;
flush_scheduled_work(); flush_scheduled_work();
down_write(&clp->cl_sem);
if (!list_empty(&server->nfs4_siblings))
list_del_init(&server->nfs4_siblings);
up_write(&clp->cl_sem);
} }
/* Must be called with clp->cl_sem locked for writes */
void void
nfs4_kill_renewd(struct nfs4_client *clp) nfs4_kill_renewd(struct nfs_client *clp)
{ {
down_read(&clp->cl_sem); down_read(&clp->cl_sem);
if (!list_empty(&clp->cl_superblocks)) {
up_read(&clp->cl_sem);
return;
}
cancel_delayed_work(&clp->cl_renewd); cancel_delayed_work(&clp->cl_renewd);
up_read(&clp->cl_sem); up_read(&clp->cl_sem);
flush_scheduled_work(); flush_scheduled_work();

View File

@ -50,149 +50,15 @@
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "callback.h" #include "callback.h"
#include "delegation.h" #include "delegation.h"
#include "internal.h"
#define OPENOWNER_POOL_SIZE 8 #define OPENOWNER_POOL_SIZE 8
const nfs4_stateid zero_stateid; const nfs4_stateid zero_stateid;
static DEFINE_SPINLOCK(state_spinlock);
static LIST_HEAD(nfs4_clientid_list); static LIST_HEAD(nfs4_clientid_list);
void static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
init_nfsv4_state(struct nfs_server *server)
{
server->nfs4_state = NULL;
INIT_LIST_HEAD(&server->nfs4_siblings);
}
void
destroy_nfsv4_state(struct nfs_server *server)
{
kfree(server->mnt_path);
server->mnt_path = NULL;
if (server->nfs4_state) {
nfs4_put_client(server->nfs4_state);
server->nfs4_state = NULL;
}
}
/*
* nfs4_get_client(): returns an empty client structure
* nfs4_put_client(): drops reference to client structure
*
* Since these are allocated/deallocated very rarely, we don't
* bother putting them in a slab cache...
*/
static struct nfs4_client *
nfs4_alloc_client(struct in_addr *addr)
{
struct nfs4_client *clp;
if (nfs_callback_up() < 0)
return NULL;
if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) {
nfs_callback_down();
return NULL;
}
memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
init_rwsem(&clp->cl_sem);
INIT_LIST_HEAD(&clp->cl_delegations);
INIT_LIST_HEAD(&clp->cl_state_owners);
INIT_LIST_HEAD(&clp->cl_unused);
spin_lock_init(&clp->cl_lock);
atomic_set(&clp->cl_count, 1);
INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
INIT_LIST_HEAD(&clp->cl_superblocks);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
clp->cl_rpcclient = ERR_PTR(-EINVAL);
clp->cl_boot_time = CURRENT_TIME;
clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
return clp;
}
static void
nfs4_free_client(struct nfs4_client *clp)
{
struct nfs4_state_owner *sp;
while (!list_empty(&clp->cl_unused)) {
sp = list_entry(clp->cl_unused.next,
struct nfs4_state_owner,
so_list);
list_del(&sp->so_list);
kfree(sp);
}
BUG_ON(!list_empty(&clp->cl_state_owners));
nfs_idmap_delete(clp);
if (!IS_ERR(clp->cl_rpcclient))
rpc_shutdown_client(clp->cl_rpcclient);
kfree(clp);
nfs_callback_down();
}
static struct nfs4_client *__nfs4_find_client(struct in_addr *addr)
{
struct nfs4_client *clp;
list_for_each_entry(clp, &nfs4_clientid_list, cl_servers) {
if (memcmp(&clp->cl_addr, addr, sizeof(clp->cl_addr)) == 0) {
atomic_inc(&clp->cl_count);
return clp;
}
}
return NULL;
}
struct nfs4_client *nfs4_find_client(struct in_addr *addr)
{
struct nfs4_client *clp;
spin_lock(&state_spinlock);
clp = __nfs4_find_client(addr);
spin_unlock(&state_spinlock);
return clp;
}
struct nfs4_client *
nfs4_get_client(struct in_addr *addr)
{
struct nfs4_client *clp, *new = NULL;
spin_lock(&state_spinlock);
for (;;) {
clp = __nfs4_find_client(addr);
if (clp != NULL)
break;
clp = new;
if (clp != NULL) {
list_add(&clp->cl_servers, &nfs4_clientid_list);
new = NULL;
break;
}
spin_unlock(&state_spinlock);
new = nfs4_alloc_client(addr);
spin_lock(&state_spinlock);
if (new == NULL)
break;
}
spin_unlock(&state_spinlock);
if (new)
nfs4_free_client(new);
return clp;
}
void
nfs4_put_client(struct nfs4_client *clp)
{
if (!atomic_dec_and_lock(&clp->cl_count, &state_spinlock))
return;
list_del(&clp->cl_servers);
spin_unlock(&state_spinlock);
BUG_ON(!list_empty(&clp->cl_superblocks));
rpc_wake_up(&clp->cl_rpcwaitq);
nfs4_kill_renewd(clp);
nfs4_free_client(clp);
}
static int nfs4_init_client(struct nfs4_client *clp, struct rpc_cred *cred)
{ {
int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK,
nfs_callback_tcpport, cred); nfs_callback_tcpport, cred);
@ -204,13 +70,13 @@ static int nfs4_init_client(struct nfs4_client *clp, struct rpc_cred *cred)
} }
u32 u32
nfs4_alloc_lockowner_id(struct nfs4_client *clp) nfs4_alloc_lockowner_id(struct nfs_client *clp)
{ {
return clp->cl_lockowner_id ++; return clp->cl_lockowner_id ++;
} }
static struct nfs4_state_owner * static struct nfs4_state_owner *
nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred) nfs4_client_grab_unused(struct nfs_client *clp, struct rpc_cred *cred)
{ {
struct nfs4_state_owner *sp = NULL; struct nfs4_state_owner *sp = NULL;
@ -224,7 +90,7 @@ nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred)
return sp; return sp;
} }
struct rpc_cred *nfs4_get_renew_cred(struct nfs4_client *clp) struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct rpc_cred *cred = NULL; struct rpc_cred *cred = NULL;
@ -238,7 +104,7 @@ struct rpc_cred *nfs4_get_renew_cred(struct nfs4_client *clp)
return cred; return cred;
} }
struct rpc_cred *nfs4_get_setclientid_cred(struct nfs4_client *clp) struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
@ -251,7 +117,7 @@ struct rpc_cred *nfs4_get_setclientid_cred(struct nfs4_client *clp)
} }
static struct nfs4_state_owner * static struct nfs4_state_owner *
nfs4_find_state_owner(struct nfs4_client *clp, struct rpc_cred *cred) nfs4_find_state_owner(struct nfs_client *clp, struct rpc_cred *cred)
{ {
struct nfs4_state_owner *sp, *res = NULL; struct nfs4_state_owner *sp, *res = NULL;
@ -294,7 +160,7 @@ nfs4_alloc_state_owner(void)
void void
nfs4_drop_state_owner(struct nfs4_state_owner *sp) nfs4_drop_state_owner(struct nfs4_state_owner *sp)
{ {
struct nfs4_client *clp = sp->so_client; struct nfs_client *clp = sp->so_client;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
list_del_init(&sp->so_list); list_del_init(&sp->so_list);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
@ -306,7 +172,7 @@ nfs4_drop_state_owner(struct nfs4_state_owner *sp)
*/ */
struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred)
{ {
struct nfs4_client *clp = server->nfs4_state; struct nfs_client *clp = server->nfs_client;
struct nfs4_state_owner *sp, *new; struct nfs4_state_owner *sp, *new;
get_rpccred(cred); get_rpccred(cred);
@ -337,7 +203,7 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct
*/ */
void nfs4_put_state_owner(struct nfs4_state_owner *sp) void nfs4_put_state_owner(struct nfs4_state_owner *sp)
{ {
struct nfs4_client *clp = sp->so_client; struct nfs_client *clp = sp->so_client;
struct rpc_cred *cred = sp->so_cred; struct rpc_cred *cred = sp->so_cred;
if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock))
@ -540,7 +406,7 @@ __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
{ {
struct nfs4_lock_state *lsp; struct nfs4_lock_state *lsp;
struct nfs4_client *clp = state->owner->so_client; struct nfs_client *clp = state->owner->so_client;
lsp = kzalloc(sizeof(*lsp), GFP_KERNEL); lsp = kzalloc(sizeof(*lsp), GFP_KERNEL);
if (lsp == NULL) if (lsp == NULL)
@ -752,7 +618,7 @@ out:
static int reclaimer(void *); static int reclaimer(void *);
static inline void nfs4_clear_recover_bit(struct nfs4_client *clp) static inline void nfs4_clear_recover_bit(struct nfs_client *clp)
{ {
smp_mb__before_clear_bit(); smp_mb__before_clear_bit();
clear_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state); clear_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state);
@ -764,25 +630,25 @@ static inline void nfs4_clear_recover_bit(struct nfs4_client *clp)
/* /*
* State recovery routine * State recovery routine
*/ */
static void nfs4_recover_state(struct nfs4_client *clp) static void nfs4_recover_state(struct nfs_client *clp)
{ {
struct task_struct *task; struct task_struct *task;
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
atomic_inc(&clp->cl_count); atomic_inc(&clp->cl_count);
task = kthread_run(reclaimer, clp, "%u.%u.%u.%u-reclaim", task = kthread_run(reclaimer, clp, "%u.%u.%u.%u-reclaim",
NIPQUAD(clp->cl_addr)); NIPQUAD(clp->cl_addr.sin_addr));
if (!IS_ERR(task)) if (!IS_ERR(task))
return; return;
nfs4_clear_recover_bit(clp); nfs4_clear_recover_bit(clp);
nfs4_put_client(clp); nfs_put_client(clp);
module_put(THIS_MODULE); module_put(THIS_MODULE);
} }
/* /*
* Schedule a state recovery attempt * Schedule a state recovery attempt
*/ */
void nfs4_schedule_state_recovery(struct nfs4_client *clp) void nfs4_schedule_state_recovery(struct nfs_client *clp)
{ {
if (!clp) if (!clp)
return; return;
@ -879,7 +745,7 @@ out_err:
return status; return status;
} }
static void nfs4_state_mark_reclaim(struct nfs4_client *clp) static void nfs4_state_mark_reclaim(struct nfs_client *clp)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct nfs4_state *state; struct nfs4_state *state;
@ -903,7 +769,7 @@ static void nfs4_state_mark_reclaim(struct nfs4_client *clp)
static int reclaimer(void *ptr) static int reclaimer(void *ptr)
{ {
struct nfs4_client *clp = ptr; struct nfs_client *clp = ptr;
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct nfs4_state_recovery_ops *ops; struct nfs4_state_recovery_ops *ops;
struct rpc_cred *cred; struct rpc_cred *cred;
@ -970,12 +836,12 @@ out:
if (status == -NFS4ERR_CB_PATH_DOWN) if (status == -NFS4ERR_CB_PATH_DOWN)
nfs_handle_cb_pathdown(clp); nfs_handle_cb_pathdown(clp);
nfs4_clear_recover_bit(clp); nfs4_clear_recover_bit(clp);
nfs4_put_client(clp); nfs_put_client(clp);
module_put_and_exit(0); module_put_and_exit(0);
return 0; return 0;
out_error: out_error:
printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n", printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n",
NIPQUAD(clp->cl_addr.s_addr), -status); NIPQUAD(clp->cl_addr.sin_addr), -status);
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
goto out; goto out;
} }

View File

@ -58,7 +58,7 @@
/* Mapping from NFS error code to "errno" error code. */ /* Mapping from NFS error code to "errno" error code. */
#define errno_NFSERR_IO EIO #define errno_NFSERR_IO EIO
static int nfs_stat_to_errno(int); static int nfs4_stat_to_errno(int);
/* NFSv4 COMPOUND tags are only wanted for debugging purposes */ /* NFSv4 COMPOUND tags are only wanted for debugging purposes */
#ifdef DEBUG #ifdef DEBUG
@ -128,7 +128,7 @@ static int nfs_stat_to_errno(int);
#define decode_link_maxsz (op_decode_hdr_maxsz + 5) #define decode_link_maxsz (op_decode_hdr_maxsz + 5)
#define encode_symlink_maxsz (op_encode_hdr_maxsz + \ #define encode_symlink_maxsz (op_encode_hdr_maxsz + \
1 + nfs4_name_maxsz + \ 1 + nfs4_name_maxsz + \
nfs4_path_maxsz + \ 1 + \
nfs4_fattr_maxsz) nfs4_fattr_maxsz)
#define decode_symlink_maxsz (op_decode_hdr_maxsz + 8) #define decode_symlink_maxsz (op_decode_hdr_maxsz + 8)
#define encode_create_maxsz (op_encode_hdr_maxsz + \ #define encode_create_maxsz (op_encode_hdr_maxsz + \
@ -529,7 +529,7 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
if (iap->ia_valid & ATTR_MODE) if (iap->ia_valid & ATTR_MODE)
len += 4; len += 4;
if (iap->ia_valid & ATTR_UID) { if (iap->ia_valid & ATTR_UID) {
owner_namelen = nfs_map_uid_to_name(server->nfs4_state, iap->ia_uid, owner_name); owner_namelen = nfs_map_uid_to_name(server->nfs_client, iap->ia_uid, owner_name);
if (owner_namelen < 0) { if (owner_namelen < 0) {
printk(KERN_WARNING "nfs: couldn't resolve uid %d to string\n", printk(KERN_WARNING "nfs: couldn't resolve uid %d to string\n",
iap->ia_uid); iap->ia_uid);
@ -541,7 +541,7 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
len += 4 + (XDR_QUADLEN(owner_namelen) << 2); len += 4 + (XDR_QUADLEN(owner_namelen) << 2);
} }
if (iap->ia_valid & ATTR_GID) { if (iap->ia_valid & ATTR_GID) {
owner_grouplen = nfs_map_gid_to_group(server->nfs4_state, iap->ia_gid, owner_group); owner_grouplen = nfs_map_gid_to_group(server->nfs_client, iap->ia_gid, owner_group);
if (owner_grouplen < 0) { if (owner_grouplen < 0) {
printk(KERN_WARNING "nfs4: couldn't resolve gid %d to string\n", printk(KERN_WARNING "nfs4: couldn't resolve gid %d to string\n",
iap->ia_gid); iap->ia_gid);
@ -673,9 +673,9 @@ static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *c
switch (create->ftype) { switch (create->ftype) {
case NF4LNK: case NF4LNK:
RESERVE_SPACE(4 + create->u.symlink->len); RESERVE_SPACE(4);
WRITE32(create->u.symlink->len); WRITE32(create->u.symlink.len);
WRITEMEM(create->u.symlink->name, create->u.symlink->len); xdr_write_pages(xdr, create->u.symlink.pages, 0, create->u.symlink.len);
break; break;
case NF4BLK: case NF4CHR: case NF4BLK: case NF4CHR:
@ -1160,7 +1160,7 @@ static int encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, con
return 0; return 0;
} }
static int encode_renew(struct xdr_stream *xdr, const struct nfs4_client *client_stateid) static int encode_renew(struct xdr_stream *xdr, const struct nfs_client *client_stateid)
{ {
uint32_t *p; uint32_t *p;
@ -1246,7 +1246,7 @@ static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclien
return 0; return 0;
} }
static int encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs4_client *client_state) static int encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_client *client_state)
{ {
uint32_t *p; uint32_t *p;
@ -1945,7 +1945,7 @@ static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, uint32_t *p, const str
/* /*
* a RENEW request * a RENEW request
*/ */
static int nfs4_xdr_enc_renew(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) static int nfs4_xdr_enc_renew(struct rpc_rqst *req, uint32_t *p, struct nfs_client *clp)
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
@ -1975,7 +1975,7 @@ static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, uint32_t *p, struct nf
/* /*
* a SETCLIENTID_CONFIRM request * a SETCLIENTID_CONFIRM request
*/ */
static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, struct nfs_client *clp)
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
@ -2127,12 +2127,12 @@ static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
} }
READ32(nfserr); READ32(nfserr);
if (nfserr != NFS_OK) if (nfserr != NFS_OK)
return -nfs_stat_to_errno(nfserr); return -nfs4_stat_to_errno(nfserr);
return 0; return 0;
} }
/* Dummy routine */ /* Dummy routine */
static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs4_client *clp) static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs_client *clp)
{ {
uint32_t *p; uint32_t *p;
unsigned int strlen; unsigned int strlen;
@ -2636,7 +2636,7 @@ static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t
return 0; return 0;
} }
static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_client *clp, int32_t *uid) static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_client *clp, int32_t *uid)
{ {
uint32_t len, *p; uint32_t len, *p;
@ -2660,7 +2660,7 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nf
return 0; return 0;
} }
static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_client *clp, int32_t *gid) static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_client *clp, int32_t *gid)
{ {
uint32_t len, *p; uint32_t len, *p;
@ -3051,9 +3051,9 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, cons
fattr->mode |= fmode; fattr->mode |= fmode;
if ((status = decode_attr_nlink(xdr, bitmap, &fattr->nlink)) != 0) if ((status = decode_attr_nlink(xdr, bitmap, &fattr->nlink)) != 0)
goto xdr_error; goto xdr_error;
if ((status = decode_attr_owner(xdr, bitmap, server->nfs4_state, &fattr->uid)) != 0) if ((status = decode_attr_owner(xdr, bitmap, server->nfs_client, &fattr->uid)) != 0)
goto xdr_error; goto xdr_error;
if ((status = decode_attr_group(xdr, bitmap, server->nfs4_state, &fattr->gid)) != 0) if ((status = decode_attr_group(xdr, bitmap, server->nfs_client, &fattr->gid)) != 0)
goto xdr_error; goto xdr_error;
if ((status = decode_attr_rdev(xdr, bitmap, &fattr->rdev)) != 0) if ((status = decode_attr_rdev(xdr, bitmap, &fattr->rdev)) != 0)
goto xdr_error; goto xdr_error;
@ -3254,7 +3254,7 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
if (decode_space_limit(xdr, &res->maxsize) < 0) if (decode_space_limit(xdr, &res->maxsize) < 0)
return -EIO; return -EIO;
} }
return decode_ace(xdr, NULL, res->server->nfs4_state); return decode_ace(xdr, NULL, res->server->nfs_client);
} }
static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
@ -3565,7 +3565,7 @@ static int decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res)
return 0; return 0;
} }
static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_client *clp) static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp)
{ {
uint32_t *p; uint32_t *p;
uint32_t opnum; uint32_t opnum;
@ -3598,7 +3598,7 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_client *clp)
READ_BUF(len); READ_BUF(len);
return -NFSERR_CLID_INUSE; return -NFSERR_CLID_INUSE;
} else } else
return -nfs_stat_to_errno(nfserr); return -nfs4_stat_to_errno(nfserr);
return 0; return 0;
} }
@ -4256,7 +4256,7 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, uint32_t *p, struct nfs_fsi
if (!status) if (!status)
status = decode_fsinfo(&xdr, fsinfo); status = decode_fsinfo(&xdr, fsinfo);
if (!status) if (!status)
status = -nfs_stat_to_errno(hdr.status); status = -nfs4_stat_to_errno(hdr.status);
return status; return status;
} }
@ -4335,7 +4335,7 @@ static int nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, uint32_t *p, void *dummy)
* a SETCLIENTID request * a SETCLIENTID request
*/ */
static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, uint32_t *p, static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, uint32_t *p,
struct nfs4_client *clp) struct nfs_client *clp)
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr; struct compound_hdr hdr;
@ -4346,7 +4346,7 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, uint32_t *p,
if (!status) if (!status)
status = decode_setclientid(&xdr, clp); status = decode_setclientid(&xdr, clp);
if (!status) if (!status)
status = -nfs_stat_to_errno(hdr.status); status = -nfs4_stat_to_errno(hdr.status);
return status; return status;
} }
@ -4368,7 +4368,7 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, s
if (!status) if (!status)
status = decode_fsinfo(&xdr, fsinfo); status = decode_fsinfo(&xdr, fsinfo);
if (!status) if (!status)
status = -nfs_stat_to_errno(hdr.status); status = -nfs4_stat_to_errno(hdr.status);
return status; return status;
} }
@ -4521,7 +4521,7 @@ static struct {
* This one is used jointly by NFSv2 and NFSv3. * This one is used jointly by NFSv2 and NFSv3.
*/ */
static int static int
nfs_stat_to_errno(int stat) nfs4_stat_to_errno(int stat)
{ {
int i; int i;
for (i = 0; nfs_errtbl[i].stat != -1; i++) { for (i = 0; nfs_errtbl[i].stat != -1; i++) {

View File

@ -66,14 +66,14 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
dprintk("%s: call getattr\n", __FUNCTION__); dprintk("%s: call getattr\n", __FUNCTION__);
nfs_fattr_init(fattr); nfs_fattr_init(fattr);
status = rpc_call_sync(server->client_sys, &msg, 0); status = rpc_call_sync(server->nfs_client->cl_rpcclient, &msg, 0);
dprintk("%s: reply getattr: %d\n", __FUNCTION__, status); dprintk("%s: reply getattr: %d\n", __FUNCTION__, status);
if (status) if (status)
return status; return status;
dprintk("%s: call statfs\n", __FUNCTION__); dprintk("%s: call statfs\n", __FUNCTION__);
msg.rpc_proc = &nfs_procedures[NFSPROC_STATFS]; msg.rpc_proc = &nfs_procedures[NFSPROC_STATFS];
msg.rpc_resp = &fsinfo; msg.rpc_resp = &fsinfo;
status = rpc_call_sync(server->client_sys, &msg, 0); status = rpc_call_sync(server->nfs_client->cl_rpcclient, &msg, 0);
dprintk("%s: reply statfs: %d\n", __FUNCTION__, status); dprintk("%s: reply statfs: %d\n", __FUNCTION__, status);
if (status) if (status)
return status; return status;
@ -425,16 +425,17 @@ nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
} }
static int static int
nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
struct iattr *sattr, struct nfs_fh *fhandle, unsigned int len, struct iattr *sattr)
struct nfs_fattr *fattr)
{ {
struct nfs_fh fhandle;
struct nfs_fattr fattr;
struct nfs_symlinkargs arg = { struct nfs_symlinkargs arg = {
.fromfh = NFS_FH(dir), .fromfh = NFS_FH(dir),
.fromname = name->name, .fromname = dentry->d_name.name,
.fromlen = name->len, .fromlen = dentry->d_name.len,
.topath = path->name, .pages = &page,
.tolen = path->len, .pathlen = len,
.sattr = sattr .sattr = sattr
}; };
struct rpc_message msg = { struct rpc_message msg = {
@ -443,13 +444,25 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
}; };
int status; int status;
if (path->len > NFS2_MAXPATHLEN) if (len > NFS2_MAXPATHLEN)
return -ENAMETOOLONG; return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
nfs_fattr_init(fattr); dprintk("NFS call symlink %s\n", dentry->d_name.name);
fhandle->size = 0;
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_mark_for_revalidate(dir); nfs_mark_for_revalidate(dir);
/*
* V2 SYMLINK requests don't return any attributes. Setting the
* filehandle size to zero indicates to nfs_instantiate that it
* should fill in the data with a LOOKUP call on the wire.
*/
if (status == 0) {
nfs_fattr_init(&fattr);
fhandle.size = 0;
status = nfs_instantiate(dentry, &fhandle, &fattr);
}
dprintk("NFS reply symlink: %d\n", status); dprintk("NFS reply symlink: %d\n", status);
return status; return status;
} }
@ -671,7 +684,7 @@ nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
} }
struct nfs_rpc_ops nfs_v2_clientops = { const struct nfs_rpc_ops nfs_v2_clientops = {
.version = 2, /* protocol version */ .version = 2, /* protocol version */
.dentry_ops = &nfs_dentry_operations, .dentry_ops = &nfs_dentry_operations,
.dir_inode_ops = &nfs_dir_inode_operations, .dir_inode_ops = &nfs_dir_inode_operations,

View File

@ -171,7 +171,7 @@ static int nfs_readpage_sync(struct nfs_open_context *ctx, struct inode *inode,
rdata->args.offset = page_offset(page) + rdata->args.pgbase; rdata->args.offset = page_offset(page) + rdata->args.pgbase;
dprintk("NFS: nfs_proc_read(%s, (%s/%Ld), %Lu, %u)\n", dprintk("NFS: nfs_proc_read(%s, (%s/%Ld), %Lu, %u)\n",
NFS_SERVER(inode)->hostname, NFS_SERVER(inode)->nfs_client->cl_hostname,
inode->i_sb->s_id, inode->i_sb->s_id,
(long long)NFS_FILEID(inode), (long long)NFS_FILEID(inode),
(unsigned long long)rdata->args.pgbase, (unsigned long long)rdata->args.pgbase,
@ -568,8 +568,13 @@ int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data)
nfs_add_stats(data->inode, NFSIOS_SERVERREADBYTES, resp->count); nfs_add_stats(data->inode, NFSIOS_SERVERREADBYTES, resp->count);
/* Is this a short read? */ if (task->tk_status < 0) {
if (task->tk_status >= 0 && resp->count < argp->count && !resp->eof) { if (task->tk_status == -ESTALE) {
set_bit(NFS_INO_STALE, &NFS_FLAGS(data->inode));
nfs_mark_for_revalidate(data->inode);
}
} else if (resp->count < argp->count && !resp->eof) {
/* This is a short read! */
nfs_inc_stats(data->inode, NFSIOS_SHORTREAD); nfs_inc_stats(data->inode, NFSIOS_SHORTREAD);
/* Has the server at least made some progress? */ /* Has the server at least made some progress? */
if (resp->count != 0) { if (resp->count != 0) {
@ -616,6 +621,10 @@ int nfs_readpage(struct file *file, struct page *page)
if (error) if (error)
goto out_error; goto out_error;
error = -ESTALE;
if (NFS_STALE(inode))
goto out_error;
if (file == NULL) { if (file == NULL) {
ctx = nfs_find_open_context(inode, NULL, FMODE_READ); ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
if (ctx == NULL) if (ctx == NULL)
@ -678,7 +687,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
}; };
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
int ret; int ret = -ESTALE;
dprintk("NFS: nfs_readpages (%s/%Ld %d)\n", dprintk("NFS: nfs_readpages (%s/%Ld %d)\n",
inode->i_sb->s_id, inode->i_sb->s_id,
@ -686,6 +695,9 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
nr_pages); nr_pages);
nfs_inc_stats(inode, NFSIOS_VFSREADPAGES); nfs_inc_stats(inode, NFSIOS_VFSREADPAGES);
if (NFS_STALE(inode))
goto out;
if (filp == NULL) { if (filp == NULL) {
desc.ctx = nfs_find_open_context(inode, NULL, FMODE_READ); desc.ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
if (desc.ctx == NULL) if (desc.ctx == NULL)
@ -701,6 +713,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
ret = err; ret = err;
} }
put_nfs_open_context(desc.ctx); put_nfs_open_context(desc.ctx);
out:
return ret; return ret;
} }

File diff suppressed because it is too large Load Diff

View File

@ -396,6 +396,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
out: out:
clear_bit(BDI_write_congested, &bdi->state); clear_bit(BDI_write_congested, &bdi->state);
wake_up_all(&nfs_write_congestion); wake_up_all(&nfs_write_congestion);
writeback_congestion_end();
return err; return err;
} }
@ -1252,7 +1253,13 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
dprintk("NFS: %4d nfs_writeback_done (status %d)\n", dprintk("NFS: %4d nfs_writeback_done (status %d)\n",
task->tk_pid, task->tk_status); task->tk_pid, task->tk_status);
/* Call the NFS version-specific code */ /*
* ->write_done will attempt to use post-op attributes to detect
* conflicting writes by other clients. A strict interpretation
* of close-to-open would allow us to continue caching even if
* another writer had changed the file, but some applications
* depend on tighter cache coherency when writing.
*/
status = NFS_PROTO(data->inode)->write_done(task, data); status = NFS_PROTO(data->inode)->write_done(task, data);
if (status != 0) if (status != 0)
return status; return status;
@ -1273,7 +1280,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
if (time_before(complain, jiffies)) { if (time_before(complain, jiffies)) {
dprintk("NFS: faulty NFS server %s:" dprintk("NFS: faulty NFS server %s:"
" (committed = %d) != (stable = %d)\n", " (committed = %d) != (stable = %d)\n",
NFS_SERVER(data->inode)->hostname, NFS_SERVER(data->inode)->nfs_client->cl_hostname,
resp->verf->committed, argp->stable); resp->verf->committed, argp->stable);
complain = jiffies + 300 * HZ; complain = jiffies + 300 * HZ;
} }

View File

@ -375,16 +375,28 @@ nfsd4_probe_callback(struct nfs4_client *clp)
{ {
struct sockaddr_in addr; struct sockaddr_in addr;
struct nfs4_callback *cb = &clp->cl_callback; struct nfs4_callback *cb = &clp->cl_callback;
struct rpc_timeout timeparms; struct rpc_timeout timeparms = {
struct rpc_xprt * xprt; .to_initval = (NFSD_LEASE_TIME/4) * HZ,
.to_retries = 5,
.to_maxval = (NFSD_LEASE_TIME/2) * HZ,
.to_exponential = 1,
};
struct rpc_program * program = &cb->cb_program; struct rpc_program * program = &cb->cb_program;
struct rpc_stat * stat = &cb->cb_stat; struct rpc_create_args args = {
struct rpc_clnt * clnt; .protocol = IPPROTO_TCP,
.address = (struct sockaddr *)&addr,
.addrsize = sizeof(addr),
.timeout = &timeparms,
.servername = clp->cl_name.data,
.program = program,
.version = nfs_cb_version[1]->number,
.authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */
.flags = (RPC_CLNT_CREATE_NOPING),
};
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
.rpc_argp = clp, .rpc_argp = clp,
}; };
char hostname[32];
int status; int status;
if (atomic_read(&cb->cb_set)) if (atomic_read(&cb->cb_set))
@ -396,51 +408,27 @@ nfsd4_probe_callback(struct nfs4_client *clp)
addr.sin_port = htons(cb->cb_port); addr.sin_port = htons(cb->cb_port);
addr.sin_addr.s_addr = htonl(cb->cb_addr); addr.sin_addr.s_addr = htonl(cb->cb_addr);
/* Initialize timeout */
timeparms.to_initval = (NFSD_LEASE_TIME/4) * HZ;
timeparms.to_retries = 0;
timeparms.to_maxval = (NFSD_LEASE_TIME/2) * HZ;
timeparms.to_exponential = 1;
/* Create RPC transport */
xprt = xprt_create_proto(IPPROTO_TCP, &addr, &timeparms);
if (IS_ERR(xprt)) {
dprintk("NFSD: couldn't create callback transport!\n");
goto out_err;
}
/* Initialize rpc_program */ /* Initialize rpc_program */
program->name = "nfs4_cb"; program->name = "nfs4_cb";
program->number = cb->cb_prog; program->number = cb->cb_prog;
program->nrvers = ARRAY_SIZE(nfs_cb_version); program->nrvers = ARRAY_SIZE(nfs_cb_version);
program->version = nfs_cb_version; program->version = nfs_cb_version;
program->stats = stat; program->stats = &cb->cb_stat;
/* Initialize rpc_stat */ /* Initialize rpc_stat */
memset(stat, 0, sizeof(struct rpc_stat)); memset(program->stats, 0, sizeof(cb->cb_stat));
stat->program = program; program->stats->program = program;
/* Create RPC client /* Create RPC client */
* cb->cb_client = rpc_create(&args);
* XXX AUTH_UNIX only - need AUTH_GSS.... if (!cb->cb_client) {
*/
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr.sin_addr.s_addr));
clnt = rpc_new_client(xprt, hostname, program, 1, RPC_AUTH_UNIX);
if (IS_ERR(clnt)) {
dprintk("NFSD: couldn't create callback client\n"); dprintk("NFSD: couldn't create callback client\n");
goto out_err; goto out_err;
} }
clnt->cl_intr = 0;
clnt->cl_softrtry = 1;
/* Kick rpciod, put the call on the wire. */ /* Kick rpciod, put the call on the wire. */
if (rpciod_up() != 0)
if (rpciod_up() != 0) {
dprintk("nfsd: couldn't start rpciod for callbacks!\n");
goto out_clnt; goto out_clnt;
}
cb->cb_client = clnt;
/* the task holds a reference to the nfs4_client struct */ /* the task holds a reference to the nfs4_client struct */
atomic_inc(&clp->cl_count); atomic_inc(&clp->cl_count);
@ -448,7 +436,7 @@ nfsd4_probe_callback(struct nfs4_client *clp)
msg.rpc_cred = nfsd4_lookupcred(clp,0); msg.rpc_cred = nfsd4_lookupcred(clp,0);
if (IS_ERR(msg.rpc_cred)) if (IS_ERR(msg.rpc_cred))
goto out_rpciod; goto out_rpciod;
status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, &nfs4_cb_null_ops, NULL); status = rpc_call_async(cb->cb_client, &msg, RPC_TASK_ASYNC, &nfs4_cb_null_ops, NULL);
put_rpccred(msg.rpc_cred); put_rpccred(msg.rpc_cred);
if (status != 0) { if (status != 0) {
@ -462,7 +450,7 @@ out_rpciod:
rpciod_down(); rpciod_down();
cb->cb_client = NULL; cb->cb_client = NULL;
out_clnt: out_clnt:
rpc_shutdown_client(clnt); rpc_shutdown_client(cb->cb_client);
out_err: out_err:
dprintk("NFSD: warning: no callback path to client %.*s\n", dprintk("NFSD: warning: no callback path to client %.*s\n",
(int)clp->cl_name.len, clp->cl_name.data); (int)clp->cl_name.len, clp->cl_name.data);

View File

@ -746,6 +746,7 @@ extern void blk_queue_free_tags(request_queue_t *);
extern int blk_queue_resize_tags(request_queue_t *, int); extern int blk_queue_resize_tags(request_queue_t *, int);
extern void blk_queue_invalidate_tags(request_queue_t *); extern void blk_queue_invalidate_tags(request_queue_t *);
extern long blk_congestion_wait(int rw, long timeout); extern long blk_congestion_wait(int rw, long timeout);
extern void blk_congestion_end(int rw);
extern void blk_rq_bio_prep(request_queue_t *, struct request *, struct bio *); extern void blk_rq_bio_prep(request_queue_t *, struct request *, struct bio *);
extern int blkdev_issue_flush(struct block_device *, sector_t *); extern int blkdev_issue_flush(struct block_device *, sector_t *);

View File

@ -221,6 +221,7 @@ static inline int dname_external(struct dentry *dentry)
*/ */
extern void d_instantiate(struct dentry *, struct inode *); extern void d_instantiate(struct dentry *, struct inode *);
extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *); extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *);
extern struct dentry * d_materialise_unique(struct dentry *, struct inode *);
extern void d_delete(struct dentry *); extern void d_delete(struct dentry *);
/* allocate/de-allocate */ /* allocate/de-allocate */

View File

@ -42,6 +42,7 @@
#include <linux/in.h> #include <linux/in.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/rbtree.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/wait.h> #include <linux/wait.h>
@ -69,6 +70,8 @@
* NFSv3/v4 Access mode cache entry * NFSv3/v4 Access mode cache entry
*/ */
struct nfs_access_entry { struct nfs_access_entry {
struct rb_node rb_node;
struct list_head lru;
unsigned long jiffies; unsigned long jiffies;
struct rpc_cred * cred; struct rpc_cred * cred;
int mask; int mask;
@ -145,7 +148,9 @@ struct nfs_inode {
*/ */
atomic_t data_updates; atomic_t data_updates;
struct nfs_access_entry cache_access; struct rb_root access_cache;
struct list_head access_cache_entry_lru;
struct list_head access_cache_inode_lru;
#ifdef CONFIG_NFS_V3_ACL #ifdef CONFIG_NFS_V3_ACL
struct posix_acl *acl_access; struct posix_acl *acl_access;
struct posix_acl *acl_default; struct posix_acl *acl_default;
@ -199,6 +204,7 @@ struct nfs_inode {
#define NFS_INO_REVALIDATING (0) /* revalidating attrs */ #define NFS_INO_REVALIDATING (0) /* revalidating attrs */
#define NFS_INO_ADVISE_RDPLUS (1) /* advise readdirplus */ #define NFS_INO_ADVISE_RDPLUS (1) /* advise readdirplus */
#define NFS_INO_STALE (2) /* possible stale inode */ #define NFS_INO_STALE (2) /* possible stale inode */
#define NFS_INO_ACL_LRU_SET (3) /* Inode is on the LRU list */
static inline struct nfs_inode *NFS_I(struct inode *inode) static inline struct nfs_inode *NFS_I(struct inode *inode)
{ {
@ -209,8 +215,7 @@ static inline struct nfs_inode *NFS_I(struct inode *inode)
#define NFS_FH(inode) (&NFS_I(inode)->fh) #define NFS_FH(inode) (&NFS_I(inode)->fh)
#define NFS_SERVER(inode) (NFS_SB(inode->i_sb)) #define NFS_SERVER(inode) (NFS_SB(inode->i_sb))
#define NFS_CLIENT(inode) (NFS_SERVER(inode)->client) #define NFS_CLIENT(inode) (NFS_SERVER(inode)->client)
#define NFS_PROTO(inode) (NFS_SERVER(inode)->rpc_ops) #define NFS_PROTO(inode) (NFS_SERVER(inode)->nfs_client->rpc_ops)
#define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode)))
#define NFS_COOKIEVERF(inode) (NFS_I(inode)->cookieverf) #define NFS_COOKIEVERF(inode) (NFS_I(inode)->cookieverf)
#define NFS_READTIME(inode) (NFS_I(inode)->read_cache_jiffies) #define NFS_READTIME(inode) (NFS_I(inode)->read_cache_jiffies)
#define NFS_CHANGE_ATTR(inode) (NFS_I(inode)->change_attr) #define NFS_CHANGE_ATTR(inode) (NFS_I(inode)->change_attr)
@ -297,6 +302,7 @@ extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int nfs_permission(struct inode *, int, struct nameidata *); extern int nfs_permission(struct inode *, int, struct nameidata *);
extern int nfs_access_get_cached(struct inode *, struct rpc_cred *, struct nfs_access_entry *); extern int nfs_access_get_cached(struct inode *, struct rpc_cred *, struct nfs_access_entry *);
extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *); extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *);
extern void nfs_access_zap_cache(struct inode *inode);
extern int nfs_open(struct inode *, struct file *); extern int nfs_open(struct inode *, struct file *);
extern int nfs_release(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *);
extern int nfs_attribute_timeout(struct inode *inode); extern int nfs_attribute_timeout(struct inode *inode);
@ -579,6 +585,7 @@ extern void * nfs_root_data(void);
#define NFSDBG_FILE 0x0040 #define NFSDBG_FILE 0x0040
#define NFSDBG_ROOT 0x0080 #define NFSDBG_ROOT 0x0080
#define NFSDBG_CALLBACK 0x0100 #define NFSDBG_CALLBACK 0x0100
#define NFSDBG_CLIENT 0x0200
#define NFSDBG_ALL 0xFFFF #define NFSDBG_ALL 0xFFFF
#ifdef __KERNEL__ #ifdef __KERNEL__

View File

@ -6,14 +6,80 @@
struct nfs_iostats; struct nfs_iostats;
/*
* The nfs_client identifies our client state to the server.
*/
struct nfs_client {
atomic_t cl_count;
int cl_cons_state; /* current construction state (-ve: init error) */
#define NFS_CS_READY 0 /* ready to be used */
#define NFS_CS_INITING 1 /* busy initialising */
int cl_nfsversion; /* NFS protocol version */
unsigned long cl_res_state; /* NFS resources state */
#define NFS_CS_RPCIOD 0 /* - rpciod started */
#define NFS_CS_CALLBACK 1 /* - callback started */
#define NFS_CS_IDMAP 2 /* - idmap started */
#define NFS_CS_RENEWD 3 /* - renewd started */
struct sockaddr_in cl_addr; /* server identifier */
char * cl_hostname; /* hostname of server */
struct list_head cl_share_link; /* link in global client list */
struct list_head cl_superblocks; /* List of nfs_server structs */
struct rpc_clnt * cl_rpcclient;
const struct nfs_rpc_ops *rpc_ops; /* NFS protocol vector */
unsigned long retrans_timeo; /* retransmit timeout */
unsigned int retrans_count; /* number of retransmit tries */
#ifdef CONFIG_NFS_V4
u64 cl_clientid; /* constant */
nfs4_verifier cl_confirm;
unsigned long cl_state;
u32 cl_lockowner_id;
/*
* The following rwsem ensures exclusive access to the server
* while we recover the state following a lease expiration.
*/
struct rw_semaphore cl_sem;
struct list_head cl_delegations;
struct list_head cl_state_owners;
struct list_head cl_unused;
int cl_nunused;
spinlock_t cl_lock;
unsigned long cl_lease_time;
unsigned long cl_last_renewal;
struct work_struct cl_renewd;
struct rpc_wait_queue cl_rpcwaitq;
/* used for the setclientid verifier */
struct timespec cl_boot_time;
/* idmapper */
struct idmap * cl_idmap;
/* Our own IP address, as a null-terminated string.
* This is used to generate the clientid, and the callback address.
*/
char cl_ipaddr[16];
unsigned char cl_id_uniquifier;
#endif
};
/* /*
* NFS client parameters stored in the superblock. * NFS client parameters stored in the superblock.
*/ */
struct nfs_server { struct nfs_server {
struct nfs_client * nfs_client; /* shared client and NFS4 state */
struct list_head client_link; /* List of other nfs_server structs
* that share the same client
*/
struct list_head master_link; /* link in master servers list */
struct rpc_clnt * client; /* RPC client handle */ struct rpc_clnt * client; /* RPC client handle */
struct rpc_clnt * client_sys; /* 2nd handle for FSINFO */
struct rpc_clnt * client_acl; /* ACL RPC client handle */ struct rpc_clnt * client_acl; /* ACL RPC client handle */
struct nfs_rpc_ops * rpc_ops; /* NFS protocol vector */
struct nfs_iostats * io_stats; /* I/O statistics */ struct nfs_iostats * io_stats; /* I/O statistics */
struct backing_dev_info backing_dev_info; struct backing_dev_info backing_dev_info;
int flags; /* various flags */ int flags; /* various flags */
@ -29,24 +95,14 @@ struct nfs_server {
unsigned int acregmax; unsigned int acregmax;
unsigned int acdirmin; unsigned int acdirmin;
unsigned int acdirmax; unsigned int acdirmax;
unsigned long retrans_timeo; /* retransmit timeout */
unsigned int retrans_count; /* number of retransmit tries */
unsigned int namelen; unsigned int namelen;
char * hostname; /* remote hostname */
struct nfs_fh fh;
struct sockaddr_in addr;
struct nfs_fsid fsid; struct nfs_fsid fsid;
__u64 maxfilesize; /* maximum file size */
unsigned long mount_time; /* when this fs was mounted */ unsigned long mount_time; /* when this fs was mounted */
dev_t s_dev; /* superblock dev numbers */
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
/* Our own IP address, as a null-terminated string.
* This is used to generate the clientid, and the callback address.
*/
char ip_addr[16];
char * mnt_path;
struct nfs4_client * nfs4_state; /* all NFSv4 state starts here */
struct list_head nfs4_siblings; /* List of other nfs_server structs
* that share the same clientid
*/
u32 attr_bitmask[2];/* V4 bitmask representing the set u32 attr_bitmask[2];/* V4 bitmask representing the set
of attributes supported on this of attributes supported on this
filesystem */ filesystem */
@ -54,6 +110,7 @@ struct nfs_server {
that are supported on this that are supported on this
filesystem */ filesystem */
#endif #endif
void (*destroy)(struct nfs_server *);
}; };
/* Server capabilities */ /* Server capabilities */

View File

@ -62,15 +62,15 @@ struct idmap_msg {
#ifdef __KERNEL__ #ifdef __KERNEL__
/* Forward declaration to make this header independent of others */ /* Forward declaration to make this header independent of others */
struct nfs4_client; struct nfs_client;
void nfs_idmap_new(struct nfs4_client *); int nfs_idmap_new(struct nfs_client *);
void nfs_idmap_delete(struct nfs4_client *); void nfs_idmap_delete(struct nfs_client *);
int nfs_map_name_to_uid(struct nfs4_client *, const char *, size_t, __u32 *); int nfs_map_name_to_uid(struct nfs_client *, const char *, size_t, __u32 *);
int nfs_map_group_to_gid(struct nfs4_client *, const char *, size_t, __u32 *); int nfs_map_group_to_gid(struct nfs_client *, const char *, size_t, __u32 *);
int nfs_map_uid_to_name(struct nfs4_client *, __u32, char *); int nfs_map_uid_to_name(struct nfs_client *, __u32, char *);
int nfs_map_gid_to_group(struct nfs4_client *, __u32, char *); int nfs_map_gid_to_group(struct nfs_client *, __u32, char *);
extern unsigned int nfs_idmap_cache_timeout; extern unsigned int nfs_idmap_cache_timeout;
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */

View File

@ -1,7 +1,6 @@
#ifndef _LINUX_NFS_XDR_H #ifndef _LINUX_NFS_XDR_H
#define _LINUX_NFS_XDR_H #define _LINUX_NFS_XDR_H
#include <linux/sunrpc/xprt.h>
#include <linux/nfsacl.h> #include <linux/nfsacl.h>
/* /*
@ -359,8 +358,8 @@ struct nfs_symlinkargs {
struct nfs_fh * fromfh; struct nfs_fh * fromfh;
const char * fromname; const char * fromname;
unsigned int fromlen; unsigned int fromlen;
const char * topath; struct page ** pages;
unsigned int tolen; unsigned int pathlen;
struct iattr * sattr; struct iattr * sattr;
}; };
@ -435,8 +434,8 @@ struct nfs3_symlinkargs {
struct nfs_fh * fromfh; struct nfs_fh * fromfh;
const char * fromname; const char * fromname;
unsigned int fromlen; unsigned int fromlen;
const char * topath; struct page ** pages;
unsigned int tolen; unsigned int pathlen;
struct iattr * sattr; struct iattr * sattr;
}; };
@ -534,7 +533,10 @@ struct nfs4_accessres {
struct nfs4_create_arg { struct nfs4_create_arg {
u32 ftype; u32 ftype;
union { union {
struct qstr * symlink; /* NF4LNK */ struct {
struct page ** pages;
unsigned int len;
} symlink; /* NF4LNK */
struct { struct {
u32 specdata1; u32 specdata1;
u32 specdata2; u32 specdata2;
@ -770,6 +772,9 @@ struct nfs_rpc_ops {
int (*getroot) (struct nfs_server *, struct nfs_fh *, int (*getroot) (struct nfs_server *, struct nfs_fh *,
struct nfs_fsinfo *); struct nfs_fsinfo *);
int (*lookupfh)(struct nfs_server *, struct nfs_fh *,
struct qstr *, struct nfs_fh *,
struct nfs_fattr *);
int (*getattr) (struct nfs_server *, struct nfs_fh *, int (*getattr) (struct nfs_server *, struct nfs_fh *,
struct nfs_fattr *); struct nfs_fattr *);
int (*setattr) (struct dentry *, struct nfs_fattr *, int (*setattr) (struct dentry *, struct nfs_fattr *,
@ -791,9 +796,8 @@ struct nfs_rpc_ops {
int (*rename) (struct inode *, struct qstr *, int (*rename) (struct inode *, struct qstr *,
struct inode *, struct qstr *); struct inode *, struct qstr *);
int (*link) (struct inode *, struct inode *, struct qstr *); int (*link) (struct inode *, struct inode *, struct qstr *);
int (*symlink) (struct inode *, struct qstr *, struct qstr *, int (*symlink) (struct inode *, struct dentry *, struct page *,
struct iattr *, struct nfs_fh *, unsigned int, struct iattr *);
struct nfs_fattr *);
int (*mkdir) (struct inode *, struct dentry *, struct iattr *); int (*mkdir) (struct inode *, struct dentry *, struct iattr *);
int (*rmdir) (struct inode *, struct qstr *); int (*rmdir) (struct inode *, struct qstr *);
int (*readdir) (struct dentry *, struct rpc_cred *, int (*readdir) (struct dentry *, struct rpc_cred *,
@ -806,6 +810,7 @@ struct nfs_rpc_ops {
struct nfs_fsinfo *); struct nfs_fsinfo *);
int (*pathconf) (struct nfs_server *, struct nfs_fh *, int (*pathconf) (struct nfs_server *, struct nfs_fh *,
struct nfs_pathconf *); struct nfs_pathconf *);
int (*set_capabilities)(struct nfs_server *, struct nfs_fh *);
u32 * (*decode_dirent)(u32 *, struct nfs_entry *, int plus); u32 * (*decode_dirent)(u32 *, struct nfs_entry *, int plus);
void (*read_setup) (struct nfs_read_data *); void (*read_setup) (struct nfs_read_data *);
int (*read_done) (struct rpc_task *, struct nfs_read_data *); int (*read_done) (struct rpc_task *, struct nfs_read_data *);
@ -829,9 +834,9 @@ struct nfs_rpc_ops {
/* /*
* Function vectors etc. for the NFS client * Function vectors etc. for the NFS client
*/ */
extern struct nfs_rpc_ops nfs_v2_clientops; extern const struct nfs_rpc_ops nfs_v2_clientops;
extern struct nfs_rpc_ops nfs_v3_clientops; extern const struct nfs_rpc_ops nfs_v3_clientops;
extern struct nfs_rpc_ops nfs_v4_clientops; extern const struct nfs_rpc_ops nfs_v4_clientops;
extern struct rpc_version nfs_version2; extern struct rpc_version nfs_version2;
extern struct rpc_version nfs_version3; extern struct rpc_version nfs_version3;
extern struct rpc_version nfs_version4; extern struct rpc_version nfs_version4;

View File

@ -18,18 +18,6 @@
#include <linux/sunrpc/timer.h> #include <linux/sunrpc/timer.h>
#include <asm/signal.h> #include <asm/signal.h>
/*
* This defines an RPC port mapping
*/
struct rpc_portmap {
__u32 pm_prog;
__u32 pm_vers;
__u32 pm_prot;
__u16 pm_port;
unsigned char pm_binding : 1; /* doing a getport() */
struct rpc_wait_queue pm_bindwait; /* waiting on getport() */
};
struct rpc_inode; struct rpc_inode;
/* /*
@ -40,7 +28,9 @@ struct rpc_clnt {
atomic_t cl_users; /* number of references */ atomic_t cl_users; /* number of references */
struct rpc_xprt * cl_xprt; /* transport */ struct rpc_xprt * cl_xprt; /* transport */
struct rpc_procinfo * cl_procinfo; /* procedure info */ struct rpc_procinfo * cl_procinfo; /* procedure info */
u32 cl_maxproc; /* max procedure number */ u32 cl_prog, /* RPC program number */
cl_vers, /* RPC version number */
cl_maxproc; /* max procedure number */
char * cl_server; /* server machine name */ char * cl_server; /* server machine name */
char * cl_protname; /* protocol name */ char * cl_protname; /* protocol name */
@ -55,7 +45,6 @@ struct rpc_clnt {
cl_dead : 1;/* abandoned */ cl_dead : 1;/* abandoned */
struct rpc_rtt * cl_rtt; /* RTO estimator data */ struct rpc_rtt * cl_rtt; /* RTO estimator data */
struct rpc_portmap * cl_pmap; /* port mapping */
int cl_nodelen; /* nodename length */ int cl_nodelen; /* nodename length */
char cl_nodename[UNX_MAXNODENAME]; char cl_nodename[UNX_MAXNODENAME];
@ -64,14 +53,8 @@ struct rpc_clnt {
struct dentry * cl_dentry; /* inode */ struct dentry * cl_dentry; /* inode */
struct rpc_clnt * cl_parent; /* Points to parent of clones */ struct rpc_clnt * cl_parent; /* Points to parent of clones */
struct rpc_rtt cl_rtt_default; struct rpc_rtt cl_rtt_default;
struct rpc_portmap cl_pmap_default;
char cl_inline_name[32]; char cl_inline_name[32];
}; };
#define cl_timeout cl_xprt->timeout
#define cl_prog cl_pmap->pm_prog
#define cl_vers cl_pmap->pm_vers
#define cl_port cl_pmap->pm_port
#define cl_prot cl_pmap->pm_prot
/* /*
* General RPC program info * General RPC program info
@ -106,24 +89,36 @@ struct rpc_procinfo {
char * p_name; /* name of procedure */ char * p_name; /* name of procedure */
}; };
#define RPC_CONGESTED(clnt) (RPCXPRT_CONGESTED((clnt)->cl_xprt))
#define RPC_PEERADDR(clnt) (&(clnt)->cl_xprt->addr)
#ifdef __KERNEL__ #ifdef __KERNEL__
struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname, struct rpc_create_args {
struct rpc_program *info, int protocol;
u32 version, rpc_authflavor_t authflavor); struct sockaddr *address;
struct rpc_clnt *rpc_new_client(struct rpc_xprt *xprt, char *servname, size_t addrsize;
struct rpc_program *info, struct rpc_timeout *timeout;
u32 version, rpc_authflavor_t authflavor); char *servername;
struct rpc_program *program;
u32 version;
rpc_authflavor_t authflavor;
unsigned long flags;
};
/* Values for "flags" field */
#define RPC_CLNT_CREATE_HARDRTRY (1UL << 0)
#define RPC_CLNT_CREATE_INTR (1UL << 1)
#define RPC_CLNT_CREATE_AUTOBIND (1UL << 2)
#define RPC_CLNT_CREATE_ONESHOT (1UL << 3)
#define RPC_CLNT_CREATE_NONPRIVPORT (1UL << 4)
#define RPC_CLNT_CREATE_NOPING (1UL << 5)
struct rpc_clnt *rpc_create(struct rpc_create_args *args);
struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *, struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,
struct rpc_program *, int); struct rpc_program *, int);
struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); struct rpc_clnt *rpc_clone_client(struct rpc_clnt *);
int rpc_shutdown_client(struct rpc_clnt *); int rpc_shutdown_client(struct rpc_clnt *);
int rpc_destroy_client(struct rpc_clnt *); int rpc_destroy_client(struct rpc_clnt *);
void rpc_release_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *);
void rpc_getport(struct rpc_task *, struct rpc_clnt *); void rpc_getport(struct rpc_task *);
int rpc_register(u32, u32, int, unsigned short, int *); int rpc_register(u32, u32, int, unsigned short, int *);
void rpc_call_setup(struct rpc_task *, struct rpc_message *, int); void rpc_call_setup(struct rpc_task *, struct rpc_message *, int);
@ -140,6 +135,8 @@ void rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
size_t rpc_max_payload(struct rpc_clnt *); size_t rpc_max_payload(struct rpc_clnt *);
void rpc_force_rebind(struct rpc_clnt *); void rpc_force_rebind(struct rpc_clnt *);
int rpc_ping(struct rpc_clnt *clnt, int flags); int rpc_ping(struct rpc_clnt *clnt, int flags);
size_t rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t);
char * rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t);
/* /*
* Helper function for NFSroot support * Helper function for NFSroot support

View File

@ -43,7 +43,7 @@ extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *);
extern struct dentry *rpc_mkdir(char *, struct rpc_clnt *); extern struct dentry *rpc_mkdir(char *, struct rpc_clnt *);
extern int rpc_rmdir(struct dentry *); extern int rpc_rmdir(struct dentry *);
extern struct dentry *rpc_mkpipe(char *, void *, struct rpc_pipe_ops *, int flags); extern struct dentry *rpc_mkpipe(struct dentry *, const char *, void *, struct rpc_pipe_ops *, int flags);
extern int rpc_unlink(struct dentry *); extern int rpc_unlink(struct dentry *);
extern struct vfsmount *rpc_get_mount(void); extern struct vfsmount *rpc_get_mount(void);
extern void rpc_put_mount(void); extern void rpc_put_mount(void);

View File

@ -127,7 +127,6 @@ struct rpc_call_ops {
*/ */
#define RPC_TASK_ASYNC 0x0001 /* is an async task */ #define RPC_TASK_ASYNC 0x0001 /* is an async task */
#define RPC_TASK_SWAPPER 0x0002 /* is swapping in/out */ #define RPC_TASK_SWAPPER 0x0002 /* is swapping in/out */
#define RPC_TASK_CHILD 0x0008 /* is child of other task */
#define RPC_CALL_MAJORSEEN 0x0020 /* major timeout seen */ #define RPC_CALL_MAJORSEEN 0x0020 /* major timeout seen */
#define RPC_TASK_ROOTCREDS 0x0040 /* force root creds */ #define RPC_TASK_ROOTCREDS 0x0040 /* force root creds */
#define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */ #define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */
@ -136,7 +135,6 @@ struct rpc_call_ops {
#define RPC_TASK_NOINTR 0x0400 /* uninterruptible task */ #define RPC_TASK_NOINTR 0x0400 /* uninterruptible task */
#define RPC_IS_ASYNC(t) ((t)->tk_flags & RPC_TASK_ASYNC) #define RPC_IS_ASYNC(t) ((t)->tk_flags & RPC_TASK_ASYNC)
#define RPC_IS_CHILD(t) ((t)->tk_flags & RPC_TASK_CHILD)
#define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER) #define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER)
#define RPC_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS) #define RPC_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS)
#define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED) #define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED)
@ -253,7 +251,6 @@ struct rpc_task *rpc_new_task(struct rpc_clnt *, int flags,
const struct rpc_call_ops *ops, void *data); const struct rpc_call_ops *ops, void *data);
struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags, struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags,
const struct rpc_call_ops *ops, void *data); const struct rpc_call_ops *ops, void *data);
struct rpc_task *rpc_new_child(struct rpc_clnt *, struct rpc_task *parent);
void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt,
int flags, const struct rpc_call_ops *ops, int flags, const struct rpc_call_ops *ops,
void *data); void *data);
@ -261,8 +258,6 @@ void rpc_release_task(struct rpc_task *);
void rpc_exit_task(struct rpc_task *); void rpc_exit_task(struct rpc_task *);
void rpc_killall_tasks(struct rpc_clnt *); void rpc_killall_tasks(struct rpc_clnt *);
int rpc_execute(struct rpc_task *); int rpc_execute(struct rpc_task *);
void rpc_run_child(struct rpc_task *parent, struct rpc_task *child,
rpc_action action);
void rpc_init_priority_wait_queue(struct rpc_wait_queue *, const char *); void rpc_init_priority_wait_queue(struct rpc_wait_queue *, const char *);
void rpc_init_wait_queue(struct rpc_wait_queue *, const char *); void rpc_init_wait_queue(struct rpc_wait_queue *, const char *);
void rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *, void rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *,

View File

@ -12,6 +12,7 @@
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/kref.h>
#include <linux/sunrpc/sched.h> #include <linux/sunrpc/sched.h>
#include <linux/sunrpc/xdr.h> #include <linux/sunrpc/xdr.h>
@ -51,6 +52,14 @@ struct rpc_timeout {
unsigned char to_exponential; unsigned char to_exponential;
}; };
enum rpc_display_format_t {
RPC_DISPLAY_ADDR = 0,
RPC_DISPLAY_PORT,
RPC_DISPLAY_PROTO,
RPC_DISPLAY_ALL,
RPC_DISPLAY_MAX,
};
struct rpc_task; struct rpc_task;
struct rpc_xprt; struct rpc_xprt;
struct seq_file; struct seq_file;
@ -103,8 +112,10 @@ struct rpc_rqst {
struct rpc_xprt_ops { struct rpc_xprt_ops {
void (*set_buffer_size)(struct rpc_xprt *xprt, size_t sndsize, size_t rcvsize); void (*set_buffer_size)(struct rpc_xprt *xprt, size_t sndsize, size_t rcvsize);
char * (*print_addr)(struct rpc_xprt *xprt, enum rpc_display_format_t format);
int (*reserve_xprt)(struct rpc_task *task); int (*reserve_xprt)(struct rpc_task *task);
void (*release_xprt)(struct rpc_xprt *xprt, struct rpc_task *task); void (*release_xprt)(struct rpc_xprt *xprt, struct rpc_task *task);
void (*rpcbind)(struct rpc_task *task);
void (*set_port)(struct rpc_xprt *xprt, unsigned short port); void (*set_port)(struct rpc_xprt *xprt, unsigned short port);
void (*connect)(struct rpc_task *task); void (*connect)(struct rpc_task *task);
void * (*buf_alloc)(struct rpc_task *task, size_t size); void * (*buf_alloc)(struct rpc_task *task, size_t size);
@ -119,12 +130,14 @@ struct rpc_xprt_ops {
}; };
struct rpc_xprt { struct rpc_xprt {
struct kref kref; /* Reference count */
struct rpc_xprt_ops * ops; /* transport methods */ struct rpc_xprt_ops * ops; /* transport methods */
struct socket * sock; /* BSD socket layer */ struct socket * sock; /* BSD socket layer */
struct sock * inet; /* INET layer */ struct sock * inet; /* INET layer */
struct rpc_timeout timeout; /* timeout parms */ struct rpc_timeout timeout; /* timeout parms */
struct sockaddr_in addr; /* server address */ struct sockaddr_storage addr; /* server address */
size_t addrlen; /* size of server address */
int prot; /* IP protocol */ int prot; /* IP protocol */
unsigned long cong; /* current congestion */ unsigned long cong; /* current congestion */
@ -138,6 +151,7 @@ struct rpc_xprt {
unsigned int tsh_size; /* size of transport specific unsigned int tsh_size; /* size of transport specific
header */ header */
struct rpc_wait_queue binding; /* requests waiting on rpcbind */
struct rpc_wait_queue sending; /* requests waiting to send */ struct rpc_wait_queue sending; /* requests waiting to send */
struct rpc_wait_queue resend; /* requests waiting to resend */ struct rpc_wait_queue resend; /* requests waiting to resend */
struct rpc_wait_queue pending; /* requests in flight */ struct rpc_wait_queue pending; /* requests in flight */
@ -205,6 +219,8 @@ struct rpc_xprt {
void (*old_data_ready)(struct sock *, int); void (*old_data_ready)(struct sock *, int);
void (*old_state_change)(struct sock *); void (*old_state_change)(struct sock *);
void (*old_write_space)(struct sock *); void (*old_write_space)(struct sock *);
char * address_strings[RPC_DISPLAY_MAX];
}; };
#define XPRT_LAST_FRAG (1 << 0) #define XPRT_LAST_FRAG (1 << 0)
@ -217,12 +233,12 @@ struct rpc_xprt {
/* /*
* Transport operations used by ULPs * Transport operations used by ULPs
*/ */
struct rpc_xprt * xprt_create_proto(int proto, struct sockaddr_in *addr, struct rpc_timeout *to);
void xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long incr); void xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long incr);
/* /*
* Generic internal transport functions * Generic internal transport functions
*/ */
struct rpc_xprt * xprt_create_transport(int proto, struct sockaddr *addr, size_t size, struct rpc_timeout *toparms);
void xprt_connect(struct rpc_task *task); void xprt_connect(struct rpc_task *task);
void xprt_reserve(struct rpc_task *task); void xprt_reserve(struct rpc_task *task);
int xprt_reserve_xprt(struct rpc_task *task); int xprt_reserve_xprt(struct rpc_task *task);
@ -234,7 +250,8 @@ int xprt_adjust_timeout(struct rpc_rqst *req);
void xprt_release_xprt(struct rpc_xprt *xprt, struct rpc_task *task); void xprt_release_xprt(struct rpc_xprt *xprt, struct rpc_task *task);
void xprt_release_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task); void xprt_release_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task);
void xprt_release(struct rpc_task *task); void xprt_release(struct rpc_task *task);
int xprt_destroy(struct rpc_xprt *xprt); struct rpc_xprt * xprt_get(struct rpc_xprt *xprt);
void xprt_put(struct rpc_xprt *xprt);
static inline u32 *xprt_skip_transport_header(struct rpc_xprt *xprt, u32 *p) static inline u32 *xprt_skip_transport_header(struct rpc_xprt *xprt, u32 *p)
{ {
@ -269,6 +286,8 @@ int xs_setup_tcp(struct rpc_xprt *xprt, struct rpc_timeout *to);
#define XPRT_CONNECTED (1) #define XPRT_CONNECTED (1)
#define XPRT_CONNECTING (2) #define XPRT_CONNECTING (2)
#define XPRT_CLOSE_WAIT (3) #define XPRT_CLOSE_WAIT (3)
#define XPRT_BOUND (4)
#define XPRT_BINDING (5)
static inline void xprt_set_connected(struct rpc_xprt *xprt) static inline void xprt_set_connected(struct rpc_xprt *xprt)
{ {
@ -312,6 +331,33 @@ static inline int xprt_test_and_set_connecting(struct rpc_xprt *xprt)
return test_and_set_bit(XPRT_CONNECTING, &xprt->state); return test_and_set_bit(XPRT_CONNECTING, &xprt->state);
} }
static inline void xprt_set_bound(struct rpc_xprt *xprt)
{
test_and_set_bit(XPRT_BOUND, &xprt->state);
}
static inline int xprt_bound(struct rpc_xprt *xprt)
{
return test_bit(XPRT_BOUND, &xprt->state);
}
static inline void xprt_clear_bound(struct rpc_xprt *xprt)
{
clear_bit(XPRT_BOUND, &xprt->state);
}
static inline void xprt_clear_binding(struct rpc_xprt *xprt)
{
smp_mb__before_clear_bit();
clear_bit(XPRT_BINDING, &xprt->state);
smp_mb__after_clear_bit();
}
static inline int xprt_test_and_set_binding(struct rpc_xprt *xprt)
{
return test_and_set_bit(XPRT_BINDING, &xprt->state);
}
#endif /* __KERNEL__*/ #endif /* __KERNEL__*/
#endif /* _LINUX_SUNRPC_XPRT_H */ #endif /* _LINUX_SUNRPC_XPRT_H */

View File

@ -85,6 +85,7 @@ int wakeup_pdflush(long nr_pages);
void laptop_io_completion(void); void laptop_io_completion(void);
void laptop_sync_completion(void); void laptop_sync_completion(void);
void throttle_vm_writeout(void); void throttle_vm_writeout(void);
void writeback_congestion_end(void);
/* These are exported to sysctl. */ /* These are exported to sysctl. */
extern int dirty_background_ratio; extern int dirty_background_ratio;

View File

@ -802,6 +802,15 @@ int test_set_page_writeback(struct page *page)
} }
EXPORT_SYMBOL(test_set_page_writeback); EXPORT_SYMBOL(test_set_page_writeback);
/*
* Wakes up tasks that are being throttled due to writeback congestion
*/
void writeback_congestion_end(void)
{
blk_congestion_end(WRITE);
}
EXPORT_SYMBOL(writeback_congestion_end);
/* /*
* Return true if any of the pages in the mapping are marged with the * Return true if any of the pages in the mapping are marged with the
* passed tag. * passed tag.

View File

@ -88,7 +88,6 @@ struct gss_auth {
struct list_head upcalls; struct list_head upcalls;
struct rpc_clnt *client; struct rpc_clnt *client;
struct dentry *dentry; struct dentry *dentry;
char path[48];
spinlock_t lock; spinlock_t lock;
}; };
@ -690,10 +689,8 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
if (err) if (err)
goto err_put_mech; goto err_put_mech;
snprintf(gss_auth->path, sizeof(gss_auth->path), "%s/%s", gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name,
clnt->cl_pathname, clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
gss_auth->mech->gm_name);
gss_auth->dentry = rpc_mkpipe(gss_auth->path, clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
if (IS_ERR(gss_auth->dentry)) { if (IS_ERR(gss_auth->dentry)) {
err = PTR_ERR(gss_auth->dentry); err = PTR_ERR(gss_auth->dentry);
goto err_put_mech; goto err_put_mech;

View File

@ -97,17 +97,7 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
} }
} }
/* static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, struct rpc_program *program, u32 vers, rpc_authflavor_t flavor)
* Create an RPC client
* FIXME: This should also take a flags argument (as in task->tk_flags).
* It's called (among others) from pmap_create_client, which may in
* turn be called by an async task. In this case, rpciod should not be
* made to sleep too long.
*/
struct rpc_clnt *
rpc_new_client(struct rpc_xprt *xprt, char *servname,
struct rpc_program *program, u32 vers,
rpc_authflavor_t flavor)
{ {
struct rpc_version *version; struct rpc_version *version;
struct rpc_clnt *clnt = NULL; struct rpc_clnt *clnt = NULL;
@ -147,16 +137,12 @@ rpc_new_client(struct rpc_xprt *xprt, char *servname,
clnt->cl_procinfo = version->procs; clnt->cl_procinfo = version->procs;
clnt->cl_maxproc = version->nrprocs; clnt->cl_maxproc = version->nrprocs;
clnt->cl_protname = program->name; clnt->cl_protname = program->name;
clnt->cl_pmap = &clnt->cl_pmap_default;
clnt->cl_port = xprt->addr.sin_port;
clnt->cl_prog = program->number; clnt->cl_prog = program->number;
clnt->cl_vers = version->number; clnt->cl_vers = version->number;
clnt->cl_prot = xprt->prot;
clnt->cl_stats = program->stats; clnt->cl_stats = program->stats;
clnt->cl_metrics = rpc_alloc_iostats(clnt); clnt->cl_metrics = rpc_alloc_iostats(clnt);
rpc_init_wait_queue(&clnt->cl_pmap_default.pm_bindwait, "bindwait");
if (!clnt->cl_port) if (!xprt_bound(clnt->cl_xprt))
clnt->cl_autobind = 1; clnt->cl_autobind = 1;
clnt->cl_rtt = &clnt->cl_rtt_default; clnt->cl_rtt = &clnt->cl_rtt_default;
@ -191,40 +177,71 @@ out_no_path:
kfree(clnt->cl_server); kfree(clnt->cl_server);
kfree(clnt); kfree(clnt);
out_err: out_err:
xprt_destroy(xprt); xprt_put(xprt);
out_no_xprt: out_no_xprt:
return ERR_PTR(err); return ERR_PTR(err);
} }
/** /*
* Create an RPC client * rpc_create - create an RPC client and transport with one call
* @xprt - pointer to xprt struct * @args: rpc_clnt create argument structure
* @servname - name of server
* @info - rpc_program
* @version - rpc_program version
* @authflavor - rpc_auth flavour to use
* *
* Creates an RPC client structure, then pings the server in order to * Creates and initializes an RPC transport and an RPC client.
* determine if it is up, and if it supports this program and version.
* *
* This function should never be called by asynchronous tasks such as * It can ping the server in order to determine if it is up, and to see if
* the portmapper. * it supports this program and version. RPC_CLNT_CREATE_NOPING disables
* this behavior so asynchronous tasks can also use rpc_create.
*/ */
struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname, struct rpc_clnt *rpc_create(struct rpc_create_args *args)
struct rpc_program *info, u32 version, rpc_authflavor_t authflavor)
{ {
struct rpc_xprt *xprt;
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
int err;
xprt = xprt_create_transport(args->protocol, args->address,
clnt = rpc_new_client(xprt, servname, info, version, authflavor); args->addrsize, args->timeout);
if (IS_ERR(xprt))
return (struct rpc_clnt *)xprt;
/*
* By default, kernel RPC client connects from a reserved port.
* CAP_NET_BIND_SERVICE will not be set for unprivileged requesters,
* but it is always enabled for rpciod, which handles the connect
* operation.
*/
xprt->resvport = 1;
if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT)
xprt->resvport = 0;
dprintk("RPC: creating %s client for %s (xprt %p)\n",
args->program->name, args->servername, xprt);
clnt = rpc_new_client(xprt, args->servername, args->program,
args->version, args->authflavor);
if (IS_ERR(clnt)) if (IS_ERR(clnt))
return clnt; return clnt;
err = rpc_ping(clnt, RPC_TASK_SOFT|RPC_TASK_NOINTR);
if (err == 0) if (!(args->flags & RPC_CLNT_CREATE_NOPING)) {
return clnt; int err = rpc_ping(clnt, RPC_TASK_SOFT|RPC_TASK_NOINTR);
rpc_shutdown_client(clnt); if (err != 0) {
return ERR_PTR(err); rpc_shutdown_client(clnt);
return ERR_PTR(err);
}
}
clnt->cl_softrtry = 1;
if (args->flags & RPC_CLNT_CREATE_HARDRTRY)
clnt->cl_softrtry = 0;
if (args->flags & RPC_CLNT_CREATE_INTR)
clnt->cl_intr = 1;
if (args->flags & RPC_CLNT_CREATE_AUTOBIND)
clnt->cl_autobind = 1;
if (args->flags & RPC_CLNT_CREATE_ONESHOT)
clnt->cl_oneshot = 1;
return clnt;
} }
EXPORT_SYMBOL_GPL(rpc_create);
/* /*
* This function clones the RPC client structure. It allows us to share the * This function clones the RPC client structure. It allows us to share the
@ -244,8 +261,7 @@ rpc_clone_client(struct rpc_clnt *clnt)
atomic_set(&new->cl_users, 0); atomic_set(&new->cl_users, 0);
new->cl_parent = clnt; new->cl_parent = clnt;
atomic_inc(&clnt->cl_count); atomic_inc(&clnt->cl_count);
/* Duplicate portmapper */ new->cl_xprt = xprt_get(clnt->cl_xprt);
rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait");
/* Turn off autobind on clones */ /* Turn off autobind on clones */
new->cl_autobind = 0; new->cl_autobind = 0;
new->cl_oneshot = 0; new->cl_oneshot = 0;
@ -255,8 +271,7 @@ rpc_clone_client(struct rpc_clnt *clnt)
rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval); rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval);
if (new->cl_auth) if (new->cl_auth)
atomic_inc(&new->cl_auth->au_count); atomic_inc(&new->cl_auth->au_count);
new->cl_pmap = &new->cl_pmap_default; new->cl_metrics = rpc_alloc_iostats(clnt);
new->cl_metrics = rpc_alloc_iostats(clnt);
return new; return new;
out_no_clnt: out_no_clnt:
printk(KERN_INFO "RPC: out of memory in %s\n", __FUNCTION__); printk(KERN_INFO "RPC: out of memory in %s\n", __FUNCTION__);
@ -323,15 +338,12 @@ rpc_destroy_client(struct rpc_clnt *clnt)
rpc_rmdir(clnt->cl_dentry); rpc_rmdir(clnt->cl_dentry);
rpc_put_mount(); rpc_put_mount();
} }
if (clnt->cl_xprt) {
xprt_destroy(clnt->cl_xprt);
clnt->cl_xprt = NULL;
}
if (clnt->cl_server != clnt->cl_inline_name) if (clnt->cl_server != clnt->cl_inline_name)
kfree(clnt->cl_server); kfree(clnt->cl_server);
out_free: out_free:
rpc_free_iostats(clnt->cl_metrics); rpc_free_iostats(clnt->cl_metrics);
clnt->cl_metrics = NULL; clnt->cl_metrics = NULL;
xprt_put(clnt->cl_xprt);
kfree(clnt); kfree(clnt);
return 0; return 0;
} }
@ -540,6 +552,40 @@ rpc_call_setup(struct rpc_task *task, struct rpc_message *msg, int flags)
task->tk_action = rpc_exit_task; task->tk_action = rpc_exit_task;
} }
/**
* rpc_peeraddr - extract remote peer address from clnt's xprt
* @clnt: RPC client structure
* @buf: target buffer
* @size: length of target buffer
*
* Returns the number of bytes that are actually in the stored address.
*/
size_t rpc_peeraddr(struct rpc_clnt *clnt, struct sockaddr *buf, size_t bufsize)
{
size_t bytes;
struct rpc_xprt *xprt = clnt->cl_xprt;
bytes = sizeof(xprt->addr);
if (bytes > bufsize)
bytes = bufsize;
memcpy(buf, &clnt->cl_xprt->addr, bytes);
return xprt->addrlen;
}
EXPORT_SYMBOL_GPL(rpc_peeraddr);
/**
* rpc_peeraddr2str - return remote peer address in printable format
* @clnt: RPC client structure
* @format: address format
*
*/
char *rpc_peeraddr2str(struct rpc_clnt *clnt, enum rpc_display_format_t format)
{
struct rpc_xprt *xprt = clnt->cl_xprt;
return xprt->ops->print_addr(xprt, format);
}
EXPORT_SYMBOL_GPL(rpc_peeraddr2str);
void void
rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize) rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize)
{ {
@ -560,7 +606,7 @@ size_t rpc_max_payload(struct rpc_clnt *clnt)
{ {
return clnt->cl_xprt->max_payload; return clnt->cl_xprt->max_payload;
} }
EXPORT_SYMBOL(rpc_max_payload); EXPORT_SYMBOL_GPL(rpc_max_payload);
/** /**
* rpc_force_rebind - force transport to check that remote port is unchanged * rpc_force_rebind - force transport to check that remote port is unchanged
@ -570,9 +616,9 @@ EXPORT_SYMBOL(rpc_max_payload);
void rpc_force_rebind(struct rpc_clnt *clnt) void rpc_force_rebind(struct rpc_clnt *clnt)
{ {
if (clnt->cl_autobind) if (clnt->cl_autobind)
clnt->cl_port = 0; xprt_clear_bound(clnt->cl_xprt);
} }
EXPORT_SYMBOL(rpc_force_rebind); EXPORT_SYMBOL_GPL(rpc_force_rebind);
/* /*
* Restart an (async) RPC call. Usually called from within the * Restart an (async) RPC call. Usually called from within the
@ -781,16 +827,16 @@ call_encode(struct rpc_task *task)
static void static void
call_bind(struct rpc_task *task) call_bind(struct rpc_task *task)
{ {
struct rpc_clnt *clnt = task->tk_client; struct rpc_xprt *xprt = task->tk_xprt;
dprintk("RPC: %4d call_bind (status %d)\n", dprintk("RPC: %4d call_bind (status %d)\n",
task->tk_pid, task->tk_status); task->tk_pid, task->tk_status);
task->tk_action = call_connect; task->tk_action = call_connect;
if (!clnt->cl_port) { if (!xprt_bound(xprt)) {
task->tk_action = call_bind_status; task->tk_action = call_bind_status;
task->tk_timeout = task->tk_xprt->bind_timeout; task->tk_timeout = xprt->bind_timeout;
rpc_getport(task, clnt); xprt->ops->rpcbind(task);
} }
} }
@ -815,15 +861,11 @@ call_bind_status(struct rpc_task *task)
dprintk("RPC: %4d remote rpcbind: RPC program/version unavailable\n", dprintk("RPC: %4d remote rpcbind: RPC program/version unavailable\n",
task->tk_pid); task->tk_pid);
rpc_delay(task, 3*HZ); rpc_delay(task, 3*HZ);
goto retry_bind; goto retry_timeout;
case -ETIMEDOUT: case -ETIMEDOUT:
dprintk("RPC: %4d rpcbind request timed out\n", dprintk("RPC: %4d rpcbind request timed out\n",
task->tk_pid); task->tk_pid);
if (RPC_IS_SOFT(task)) { goto retry_timeout;
status = -EIO;
break;
}
goto retry_bind;
case -EPFNOSUPPORT: case -EPFNOSUPPORT:
dprintk("RPC: %4d remote rpcbind service unavailable\n", dprintk("RPC: %4d remote rpcbind service unavailable\n",
task->tk_pid); task->tk_pid);
@ -836,16 +878,13 @@ call_bind_status(struct rpc_task *task)
dprintk("RPC: %4d unrecognized rpcbind error (%d)\n", dprintk("RPC: %4d unrecognized rpcbind error (%d)\n",
task->tk_pid, -task->tk_status); task->tk_pid, -task->tk_status);
status = -EIO; status = -EIO;
break;
} }
rpc_exit(task, status); rpc_exit(task, status);
return; return;
retry_bind: retry_timeout:
task->tk_status = 0; task->tk_action = call_timeout;
task->tk_action = call_bind;
return;
} }
/* /*
@ -893,14 +932,16 @@ call_connect_status(struct rpc_task *task)
switch (status) { switch (status) {
case -ENOTCONN: case -ENOTCONN:
case -ETIMEDOUT:
case -EAGAIN: case -EAGAIN:
task->tk_action = call_bind; task->tk_action = call_bind;
break; if (!RPC_IS_SOFT(task))
default: return;
rpc_exit(task, -EIO); /* if soft mounted, test if we've timed out */
break; case -ETIMEDOUT:
task->tk_action = call_timeout;
return;
} }
rpc_exit(task, -EIO);
} }
/* /*
@ -982,6 +1023,14 @@ call_status(struct rpc_task *task)
task->tk_status = 0; task->tk_status = 0;
switch(status) { switch(status) {
case -EHOSTDOWN:
case -EHOSTUNREACH:
case -ENETUNREACH:
/*
* Delay any retries for 3 seconds, then handle as if it
* were a timeout.
*/
rpc_delay(task, 3*HZ);
case -ETIMEDOUT: case -ETIMEDOUT:
task->tk_action = call_timeout; task->tk_action = call_timeout;
break; break;
@ -1001,7 +1050,6 @@ call_status(struct rpc_task *task)
printk("%s: RPC call returned error %d\n", printk("%s: RPC call returned error %d\n",
clnt->cl_protname, -status); clnt->cl_protname, -status);
rpc_exit(task, status); rpc_exit(task, status);
break;
} }
} }
@ -1069,10 +1117,10 @@ call_decode(struct rpc_task *task)
clnt->cl_stats->rpcretrans++; clnt->cl_stats->rpcretrans++;
goto out_retry; goto out_retry;
} }
printk(KERN_WARNING "%s: too small RPC reply size (%d bytes)\n", dprintk("%s: too small RPC reply size (%d bytes)\n",
clnt->cl_protname, task->tk_status); clnt->cl_protname, task->tk_status);
rpc_exit(task, -EIO); task->tk_action = call_timeout;
return; goto out_retry;
} }
/* /*

View File

@ -1,7 +1,9 @@
/* /*
* linux/net/sunrpc/pmap.c * linux/net/sunrpc/pmap_clnt.c
* *
* Portmapper client. * In-kernel RPC portmapper client.
*
* Portmapper supports version 2 of the rpcbind protocol (RFC 1833).
* *
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/ */
@ -13,7 +15,6 @@
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xprt.h>
#include <linux/sunrpc/sched.h> #include <linux/sunrpc/sched.h>
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
@ -24,80 +25,141 @@
#define PMAP_UNSET 2 #define PMAP_UNSET 2
#define PMAP_GETPORT 3 #define PMAP_GETPORT 3
struct portmap_args {
u32 pm_prog;
u32 pm_vers;
u32 pm_prot;
unsigned short pm_port;
struct rpc_xprt * pm_xprt;
};
static struct rpc_procinfo pmap_procedures[]; static struct rpc_procinfo pmap_procedures[];
static struct rpc_clnt * pmap_create(char *, struct sockaddr_in *, int, int); static struct rpc_clnt * pmap_create(char *, struct sockaddr_in *, int, int);
static void pmap_getport_done(struct rpc_task *); static void pmap_getport_done(struct rpc_task *, void *);
static struct rpc_program pmap_program; static struct rpc_program pmap_program;
static DEFINE_SPINLOCK(pmap_lock);
/* static void pmap_getport_prepare(struct rpc_task *task, void *calldata)
* Obtain the port for a given RPC service on a given host. This one can
* be called for an ongoing RPC request.
*/
void
rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt)
{ {
struct rpc_portmap *map = clnt->cl_pmap; struct portmap_args *map = calldata;
struct sockaddr_in *sap = &clnt->cl_xprt->addr;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &pmap_procedures[PMAP_GETPORT], .rpc_proc = &pmap_procedures[PMAP_GETPORT],
.rpc_argp = map, .rpc_argp = map,
.rpc_resp = &clnt->cl_port, .rpc_resp = &map->pm_port,
.rpc_cred = NULL
}; };
struct rpc_clnt *pmap_clnt;
struct rpc_task *child;
dprintk("RPC: %4d rpc_getport(%s, %d, %d, %d)\n", rpc_call_setup(task, &msg, 0);
}
static inline struct portmap_args *pmap_map_alloc(void)
{
return kmalloc(sizeof(struct portmap_args), GFP_NOFS);
}
static inline void pmap_map_free(struct portmap_args *map)
{
kfree(map);
}
static void pmap_map_release(void *data)
{
pmap_map_free(data);
}
static const struct rpc_call_ops pmap_getport_ops = {
.rpc_call_prepare = pmap_getport_prepare,
.rpc_call_done = pmap_getport_done,
.rpc_release = pmap_map_release,
};
static inline void pmap_wake_portmap_waiters(struct rpc_xprt *xprt, int status)
{
xprt_clear_binding(xprt);
rpc_wake_up_status(&xprt->binding, status);
}
/**
* rpc_getport - obtain the port for a given RPC service on a given host
* @task: task that is waiting for portmapper request
*
* This one can be called for an ongoing RPC request, and can be used in
* an async (rpciod) context.
*/
void rpc_getport(struct rpc_task *task)
{
struct rpc_clnt *clnt = task->tk_client;
struct rpc_xprt *xprt = task->tk_xprt;
struct sockaddr_in addr;
struct portmap_args *map;
struct rpc_clnt *pmap_clnt;
struct rpc_task *child;
int status;
dprintk("RPC: %4d rpc_getport(%s, %u, %u, %d)\n",
task->tk_pid, clnt->cl_server, task->tk_pid, clnt->cl_server,
map->pm_prog, map->pm_vers, map->pm_prot); clnt->cl_prog, clnt->cl_vers, xprt->prot);
/* Autobind on cloned rpc clients is discouraged */ /* Autobind on cloned rpc clients is discouraged */
BUG_ON(clnt->cl_parent != clnt); BUG_ON(clnt->cl_parent != clnt);
spin_lock(&pmap_lock); if (xprt_test_and_set_binding(xprt)) {
if (map->pm_binding) { task->tk_status = -EACCES; /* tell caller to check again */
rpc_sleep_on(&map->pm_bindwait, task, NULL, NULL); rpc_sleep_on(&xprt->binding, task, NULL, NULL);
spin_unlock(&pmap_lock);
return; return;
} }
map->pm_binding = 1;
spin_unlock(&pmap_lock);
pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot, 0); /* Someone else may have bound if we slept */
if (IS_ERR(pmap_clnt)) { status = 0;
task->tk_status = PTR_ERR(pmap_clnt); if (xprt_bound(xprt))
goto bailout; goto bailout_nofree;
}
task->tk_status = 0;
/* status = -ENOMEM;
* Note: rpc_new_child will release client after a failure. map = pmap_map_alloc();
*/ if (!map)
if (!(child = rpc_new_child(pmap_clnt, task))) goto bailout_nofree;
map->pm_prog = clnt->cl_prog;
map->pm_vers = clnt->cl_vers;
map->pm_prot = xprt->prot;
map->pm_port = 0;
map->pm_xprt = xprt_get(xprt);
rpc_peeraddr(clnt, (struct sockaddr *) &addr, sizeof(addr));
pmap_clnt = pmap_create(clnt->cl_server, &addr, map->pm_prot, 0);
status = PTR_ERR(pmap_clnt);
if (IS_ERR(pmap_clnt))
goto bailout; goto bailout;
/* Setup the call info struct */ status = -EIO;
rpc_call_setup(child, &msg, 0); child = rpc_run_task(pmap_clnt, RPC_TASK_ASYNC, &pmap_getport_ops, map);
if (IS_ERR(child))
goto bailout;
rpc_release_task(child);
rpc_sleep_on(&xprt->binding, task, NULL, NULL);
/* ... and run the child task */
task->tk_xprt->stat.bind_count++; task->tk_xprt->stat.bind_count++;
rpc_run_child(task, child, pmap_getport_done);
return; return;
bailout: bailout:
spin_lock(&pmap_lock); pmap_map_free(map);
map->pm_binding = 0; xprt_put(xprt);
rpc_wake_up(&map->pm_bindwait); bailout_nofree:
spin_unlock(&pmap_lock); task->tk_status = status;
rpc_exit(task, -EIO); pmap_wake_portmap_waiters(xprt, status);
} }
#ifdef CONFIG_ROOT_NFS #ifdef CONFIG_ROOT_NFS
int /**
rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot) * rpc_getport_external - obtain the port for a given RPC service on a given host
* @sin: address of remote peer
* @prog: RPC program number to bind
* @vers: RPC version number to bind
* @prot: transport protocol to use to make this request
*
* This one is called from outside the RPC client in a synchronous task context.
*/
int rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot)
{ {
struct rpc_portmap map = { struct portmap_args map = {
.pm_prog = prog, .pm_prog = prog,
.pm_vers = vers, .pm_vers = vers,
.pm_prot = prot, .pm_prot = prot,
@ -112,7 +174,7 @@ rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot)
char hostname[32]; char hostname[32];
int status; int status;
dprintk("RPC: rpc_getport_external(%u.%u.%u.%u, %d, %d, %d)\n", dprintk("RPC: rpc_getport_external(%u.%u.%u.%u, %u, %u, %d)\n",
NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot); NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot);
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr)); sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr));
@ -132,45 +194,53 @@ rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot)
} }
#endif #endif
static void /*
pmap_getport_done(struct rpc_task *task) * Portmapper child task invokes this callback via tk_exit.
*/
static void pmap_getport_done(struct rpc_task *child, void *data)
{ {
struct rpc_clnt *clnt = task->tk_client; struct portmap_args *map = data;
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = map->pm_xprt;
struct rpc_portmap *map = clnt->cl_pmap; int status = child->tk_status;
dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n", if (status < 0) {
task->tk_pid, task->tk_status, clnt->cl_port); /* Portmapper not available */
xprt->ops->set_port(xprt, 0);
xprt->ops->set_port(xprt, 0); } else if (map->pm_port == 0) {
if (task->tk_status < 0) { /* Requested RPC service wasn't registered */
/* Make the calling task exit with an error */ xprt->ops->set_port(xprt, 0);
task->tk_action = rpc_exit_task; status = -EACCES;
} else if (clnt->cl_port == 0) {
/* Program not registered */
rpc_exit(task, -EACCES);
} else { } else {
xprt->ops->set_port(xprt, clnt->cl_port); /* Succeeded */
clnt->cl_port = htons(clnt->cl_port); xprt->ops->set_port(xprt, map->pm_port);
xprt_set_bound(xprt);
status = 0;
} }
spin_lock(&pmap_lock);
map->pm_binding = 0; dprintk("RPC: %4d pmap_getport_done(status %d, port %u)\n",
rpc_wake_up(&map->pm_bindwait); child->tk_pid, status, map->pm_port);
spin_unlock(&pmap_lock);
pmap_wake_portmap_waiters(xprt, status);
xprt_put(xprt);
} }
/* /**
* Set or unset a port registration with the local portmapper. * rpc_register - set or unset a port registration with the local portmapper
* @prog: RPC program number to bind
* @vers: RPC version number to bind
* @prot: transport protocol to use to make this request
* @port: port value to register
* @okay: result code
*
* port == 0 means unregister, port != 0 means register. * port == 0 means unregister, port != 0 means register.
*/ */
int int rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
{ {
struct sockaddr_in sin = { struct sockaddr_in sin = {
.sin_family = AF_INET, .sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK), .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
}; };
struct rpc_portmap map = { struct portmap_args map = {
.pm_prog = prog, .pm_prog = prog,
.pm_vers = vers, .pm_vers = vers,
.pm_prot = prot, .pm_prot = prot,
@ -184,7 +254,7 @@ rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
struct rpc_clnt *pmap_clnt; struct rpc_clnt *pmap_clnt;
int error = 0; int error = 0;
dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n", dprintk("RPC: registering (%u, %u, %d, %u) with portmapper.\n",
prog, vers, prot, port); prog, vers, prot, port);
pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP, 1); pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP, 1);
@ -207,38 +277,32 @@ rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
return error; return error;
} }
static struct rpc_clnt * static struct rpc_clnt *pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto, int privileged)
pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto, int privileged)
{ {
struct rpc_xprt *xprt; struct rpc_create_args args = {
struct rpc_clnt *clnt; .protocol = proto,
.address = (struct sockaddr *)srvaddr,
.addrsize = sizeof(*srvaddr),
.servername = hostname,
.program = &pmap_program,
.version = RPC_PMAP_VERSION,
.authflavor = RPC_AUTH_UNIX,
.flags = (RPC_CLNT_CREATE_ONESHOT |
RPC_CLNT_CREATE_NOPING),
};
/* printk("pmap: create xprt\n"); */ srvaddr->sin_port = htons(RPC_PMAP_PORT);
xprt = xprt_create_proto(proto, srvaddr, NULL);
if (IS_ERR(xprt))
return (struct rpc_clnt *)xprt;
xprt->ops->set_port(xprt, RPC_PMAP_PORT);
if (!privileged) if (!privileged)
xprt->resvport = 0; args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
return rpc_create(&args);
/* printk("pmap: create clnt\n"); */
clnt = rpc_new_client(xprt, hostname,
&pmap_program, RPC_PMAP_VERSION,
RPC_AUTH_UNIX);
if (!IS_ERR(clnt)) {
clnt->cl_softrtry = 1;
clnt->cl_oneshot = 1;
}
return clnt;
} }
/* /*
* XDR encode/decode functions for PMAP * XDR encode/decode functions for PMAP
*/ */
static int static int xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct portmap_args *map)
xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct rpc_portmap *map)
{ {
dprintk("RPC: xdr_encode_mapping(%d, %d, %d, %d)\n", dprintk("RPC: xdr_encode_mapping(%u, %u, %u, %u)\n",
map->pm_prog, map->pm_vers, map->pm_prot, map->pm_port); map->pm_prog, map->pm_vers, map->pm_prot, map->pm_port);
*p++ = htonl(map->pm_prog); *p++ = htonl(map->pm_prog);
*p++ = htonl(map->pm_vers); *p++ = htonl(map->pm_vers);
@ -249,15 +313,13 @@ xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct rpc_portmap *map)
return 0; return 0;
} }
static int static int xdr_decode_port(struct rpc_rqst *req, u32 *p, unsigned short *portp)
xdr_decode_port(struct rpc_rqst *req, u32 *p, unsigned short *portp)
{ {
*portp = (unsigned short) ntohl(*p++); *portp = (unsigned short) ntohl(*p++);
return 0; return 0;
} }
static int static int xdr_decode_bool(struct rpc_rqst *req, u32 *p, unsigned int *boolp)
xdr_decode_bool(struct rpc_rqst *req, u32 *p, unsigned int *boolp)
{ {
*boolp = (unsigned int) ntohl(*p++); *boolp = (unsigned int) ntohl(*p++);
return 0; return 0;

View File

@ -327,10 +327,8 @@ rpc_show_info(struct seq_file *m, void *v)
seq_printf(m, "RPC server: %s\n", clnt->cl_server); seq_printf(m, "RPC server: %s\n", clnt->cl_server);
seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_protname, seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_protname,
clnt->cl_prog, clnt->cl_vers); clnt->cl_prog, clnt->cl_vers);
seq_printf(m, "address: %u.%u.%u.%u\n", seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR));
NIPQUAD(clnt->cl_xprt->addr.sin_addr.s_addr)); seq_printf(m, "protocol: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PROTO));
seq_printf(m, "protocol: %s\n",
clnt->cl_xprt->prot == IPPROTO_UDP ? "udp" : "tcp");
return 0; return 0;
} }
@ -623,17 +621,13 @@ __rpc_rmdir(struct inode *dir, struct dentry *dentry)
} }
static struct dentry * static struct dentry *
rpc_lookup_negative(char *path, struct nameidata *nd) rpc_lookup_create(struct dentry *parent, const char *name, int len)
{ {
struct inode *dir = parent->d_inode;
struct dentry *dentry; struct dentry *dentry;
struct inode *dir;
int error;
if ((error = rpc_lookup_parent(path, nd)) != 0)
return ERR_PTR(error);
dir = nd->dentry->d_inode;
mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
dentry = lookup_one_len(nd->last.name, nd->dentry, nd->last.len); dentry = lookup_one_len(name, parent, len);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
goto out_err; goto out_err;
if (dentry->d_inode) { if (dentry->d_inode) {
@ -644,7 +638,20 @@ rpc_lookup_negative(char *path, struct nameidata *nd)
return dentry; return dentry;
out_err: out_err:
mutex_unlock(&dir->i_mutex); mutex_unlock(&dir->i_mutex);
rpc_release_path(nd); return dentry;
}
static struct dentry *
rpc_lookup_negative(char *path, struct nameidata *nd)
{
struct dentry *dentry;
int error;
if ((error = rpc_lookup_parent(path, nd)) != 0)
return ERR_PTR(error);
dentry = rpc_lookup_create(nd->dentry, nd->last.name, nd->last.len);
if (IS_ERR(dentry))
rpc_release_path(nd);
return dentry; return dentry;
} }
@ -703,18 +710,17 @@ rpc_rmdir(struct dentry *dentry)
} }
struct dentry * struct dentry *
rpc_mkpipe(char *path, void *private, struct rpc_pipe_ops *ops, int flags) rpc_mkpipe(struct dentry *parent, const char *name, void *private, struct rpc_pipe_ops *ops, int flags)
{ {
struct nameidata nd;
struct dentry *dentry; struct dentry *dentry;
struct inode *dir, *inode; struct inode *dir, *inode;
struct rpc_inode *rpci; struct rpc_inode *rpci;
dentry = rpc_lookup_negative(path, &nd); dentry = rpc_lookup_create(parent, name, strlen(name));
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return dentry; return dentry;
dir = nd.dentry->d_inode; dir = parent->d_inode;
inode = rpc_get_inode(dir->i_sb, S_IFSOCK | S_IRUSR | S_IWUSR); inode = rpc_get_inode(dir->i_sb, S_IFIFO | S_IRUSR | S_IWUSR);
if (!inode) if (!inode)
goto err_dput; goto err_dput;
inode->i_ino = iunique(dir->i_sb, 100); inode->i_ino = iunique(dir->i_sb, 100);
@ -728,13 +734,13 @@ rpc_mkpipe(char *path, void *private, struct rpc_pipe_ops *ops, int flags)
dget(dentry); dget(dentry);
out: out:
mutex_unlock(&dir->i_mutex); mutex_unlock(&dir->i_mutex);
rpc_release_path(&nd);
return dentry; return dentry;
err_dput: err_dput:
dput(dentry); dput(dentry);
dentry = ERR_PTR(-ENOMEM); dentry = ERR_PTR(-ENOMEM);
printk(KERN_WARNING "%s: %s() failed to create pipe %s (errno = %d)\n", printk(KERN_WARNING "%s: %s() failed to create pipe %s/%s (errno = %d)\n",
__FILE__, __FUNCTION__, path, -ENOMEM); __FILE__, __FUNCTION__, parent->d_name.name, name,
-ENOMEM);
goto out; goto out;
} }

View File

@ -21,7 +21,6 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xprt.h>
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
#define RPCDBG_FACILITY RPCDBG_SCHED #define RPCDBG_FACILITY RPCDBG_SCHED
@ -44,12 +43,6 @@ static void __rpc_default_timer(struct rpc_task *task);
static void rpciod_killall(void); static void rpciod_killall(void);
static void rpc_async_schedule(void *); static void rpc_async_schedule(void *);
/*
* RPC tasks that create another task (e.g. for contacting the portmapper)
* will wait on this queue for their child's completion
*/
static RPC_WAITQ(childq, "childq");
/* /*
* RPC tasks sit here while waiting for conditions to improve. * RPC tasks sit here while waiting for conditions to improve.
*/ */
@ -323,16 +316,6 @@ static void rpc_make_runnable(struct rpc_task *task)
wake_up_bit(&task->tk_runstate, RPC_TASK_QUEUED); wake_up_bit(&task->tk_runstate, RPC_TASK_QUEUED);
} }
/*
* Place a newly initialized task on the workqueue.
*/
static inline void
rpc_schedule_run(struct rpc_task *task)
{
rpc_set_active(task);
rpc_make_runnable(task);
}
/* /*
* Prepare for sleeping on a wait queue. * Prepare for sleeping on a wait queue.
* By always appending tasks to the list we ensure FIFO behavior. * By always appending tasks to the list we ensure FIFO behavior.
@ -559,24 +542,20 @@ void rpc_wake_up_status(struct rpc_wait_queue *queue, int status)
spin_unlock_bh(&queue->lock); spin_unlock_bh(&queue->lock);
} }
static void __rpc_atrun(struct rpc_task *task)
{
rpc_wake_up_task(task);
}
/* /*
* Run a task at a later time * Run a task at a later time
*/ */
static void __rpc_atrun(struct rpc_task *); void rpc_delay(struct rpc_task *task, unsigned long delay)
void
rpc_delay(struct rpc_task *task, unsigned long delay)
{ {
task->tk_timeout = delay; task->tk_timeout = delay;
rpc_sleep_on(&delay_queue, task, NULL, __rpc_atrun); rpc_sleep_on(&delay_queue, task, NULL, __rpc_atrun);
} }
static void
__rpc_atrun(struct rpc_task *task)
{
task->tk_status = 0;
rpc_wake_up_task(task);
}
/* /*
* Helper to call task->tk_ops->rpc_call_prepare * Helper to call task->tk_ops->rpc_call_prepare
*/ */
@ -933,72 +912,6 @@ struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags,
} }
EXPORT_SYMBOL(rpc_run_task); EXPORT_SYMBOL(rpc_run_task);
/**
* rpc_find_parent - find the parent of a child task.
* @child: child task
* @parent: parent task
*
* Checks that the parent task is still sleeping on the
* queue 'childq'. If so returns a pointer to the parent.
* Upon failure returns NULL.
*
* Caller must hold childq.lock
*/
static inline struct rpc_task *rpc_find_parent(struct rpc_task *child, struct rpc_task *parent)
{
struct rpc_task *task;
struct list_head *le;
task_for_each(task, le, &childq.tasks[0])
if (task == parent)
return parent;
return NULL;
}
static void rpc_child_exit(struct rpc_task *child, void *calldata)
{
struct rpc_task *parent;
spin_lock_bh(&childq.lock);
if ((parent = rpc_find_parent(child, calldata)) != NULL) {
parent->tk_status = child->tk_status;
__rpc_wake_up_task(parent);
}
spin_unlock_bh(&childq.lock);
}
static const struct rpc_call_ops rpc_child_ops = {
.rpc_call_done = rpc_child_exit,
};
/*
* Note: rpc_new_task releases the client after a failure.
*/
struct rpc_task *
rpc_new_child(struct rpc_clnt *clnt, struct rpc_task *parent)
{
struct rpc_task *task;
task = rpc_new_task(clnt, RPC_TASK_ASYNC | RPC_TASK_CHILD, &rpc_child_ops, parent);
if (!task)
goto fail;
return task;
fail:
parent->tk_status = -ENOMEM;
return NULL;
}
void rpc_run_child(struct rpc_task *task, struct rpc_task *child, rpc_action func)
{
spin_lock_bh(&childq.lock);
/* N.B. Is it possible for the child to have already finished? */
__rpc_sleep_on(&childq, task, func, NULL);
rpc_schedule_run(child);
spin_unlock_bh(&childq.lock);
}
/* /*
* Kill all tasks for the given client. * Kill all tasks for the given client.
* XXX: kill their descendants as well? * XXX: kill their descendants as well?

View File

@ -36,8 +36,6 @@ EXPORT_SYMBOL(rpc_wake_up_status);
EXPORT_SYMBOL(rpc_release_task); EXPORT_SYMBOL(rpc_release_task);
/* RPC client functions */ /* RPC client functions */
EXPORT_SYMBOL(rpc_create_client);
EXPORT_SYMBOL(rpc_new_client);
EXPORT_SYMBOL(rpc_clone_client); EXPORT_SYMBOL(rpc_clone_client);
EXPORT_SYMBOL(rpc_bind_new_program); EXPORT_SYMBOL(rpc_bind_new_program);
EXPORT_SYMBOL(rpc_destroy_client); EXPORT_SYMBOL(rpc_destroy_client);
@ -57,7 +55,6 @@ EXPORT_SYMBOL(rpc_queue_upcall);
EXPORT_SYMBOL(rpc_mkpipe); EXPORT_SYMBOL(rpc_mkpipe);
/* Client transport */ /* Client transport */
EXPORT_SYMBOL(xprt_create_proto);
EXPORT_SYMBOL(xprt_set_timeout); EXPORT_SYMBOL(xprt_set_timeout);
/* Client credential cache */ /* Client credential cache */

View File

@ -19,8 +19,6 @@
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xprt.h>
#include <linux/sunrpc/timer.h>
#define RPC_RTO_MAX (60*HZ) #define RPC_RTO_MAX (60*HZ)
#define RPC_RTO_INIT (HZ/5) #define RPC_RTO_INIT (HZ/5)

View File

@ -534,7 +534,7 @@ void xprt_connect(struct rpc_task *task)
dprintk("RPC: %4d xprt_connect xprt %p %s connected\n", task->tk_pid, dprintk("RPC: %4d xprt_connect xprt %p %s connected\n", task->tk_pid,
xprt, (xprt_connected(xprt) ? "is" : "is not")); xprt, (xprt_connected(xprt) ? "is" : "is not"));
if (!xprt->addr.sin_port) { if (!xprt_bound(xprt)) {
task->tk_status = -EIO; task->tk_status = -EIO;
return; return;
} }
@ -585,13 +585,6 @@ static void xprt_connect_status(struct rpc_task *task)
task->tk_pid, -task->tk_status, task->tk_client->cl_server); task->tk_pid, -task->tk_status, task->tk_client->cl_server);
xprt_release_write(xprt, task); xprt_release_write(xprt, task);
task->tk_status = -EIO; task->tk_status = -EIO;
return;
}
/* if soft mounted, just cause this RPC to fail */
if (RPC_IS_SOFT(task)) {
xprt_release_write(xprt, task);
task->tk_status = -EIO;
} }
} }
@ -829,6 +822,7 @@ static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
req->rq_bufsize = 0; req->rq_bufsize = 0;
req->rq_xid = xprt_alloc_xid(xprt); req->rq_xid = xprt_alloc_xid(xprt);
req->rq_release_snd_buf = NULL; req->rq_release_snd_buf = NULL;
xprt_reset_majortimeo(req);
dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid,
req, ntohl(req->rq_xid)); req, ntohl(req->rq_xid));
} }
@ -887,16 +881,32 @@ void xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long i
to->to_exponential = 0; to->to_exponential = 0;
} }
static struct rpc_xprt *xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to) /**
* xprt_create_transport - create an RPC transport
* @proto: requested transport protocol
* @ap: remote peer address
* @size: length of address
* @to: timeout parameters
*
*/
struct rpc_xprt *xprt_create_transport(int proto, struct sockaddr *ap, size_t size, struct rpc_timeout *to)
{ {
int result; int result;
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
struct rpc_rqst *req; struct rpc_rqst *req;
if ((xprt = kzalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL) if ((xprt = kzalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL) {
dprintk("RPC: xprt_create_transport: no memory\n");
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
xprt->addr = *ap; if (size <= sizeof(xprt->addr)) {
memcpy(&xprt->addr, ap, size);
xprt->addrlen = size;
} else {
kfree(xprt);
dprintk("RPC: xprt_create_transport: address too large\n");
return ERR_PTR(-EBADF);
}
switch (proto) { switch (proto) {
case IPPROTO_UDP: case IPPROTO_UDP:
@ -908,14 +918,15 @@ static struct rpc_xprt *xprt_setup(int proto, struct sockaddr_in *ap, struct rpc
default: default:
printk(KERN_ERR "RPC: unrecognized transport protocol: %d\n", printk(KERN_ERR "RPC: unrecognized transport protocol: %d\n",
proto); proto);
result = -EIO; return ERR_PTR(-EIO);
break;
} }
if (result) { if (result) {
kfree(xprt); kfree(xprt);
dprintk("RPC: xprt_create_transport: failed, %d\n", result);
return ERR_PTR(result); return ERR_PTR(result);
} }
kref_init(&xprt->kref);
spin_lock_init(&xprt->transport_lock); spin_lock_init(&xprt->transport_lock);
spin_lock_init(&xprt->reserve_lock); spin_lock_init(&xprt->reserve_lock);
@ -928,6 +939,7 @@ static struct rpc_xprt *xprt_setup(int proto, struct sockaddr_in *ap, struct rpc
xprt->last_used = jiffies; xprt->last_used = jiffies;
xprt->cwnd = RPC_INITCWND; xprt->cwnd = RPC_INITCWND;
rpc_init_wait_queue(&xprt->binding, "xprt_binding");
rpc_init_wait_queue(&xprt->pending, "xprt_pending"); rpc_init_wait_queue(&xprt->pending, "xprt_pending");
rpc_init_wait_queue(&xprt->sending, "xprt_sending"); rpc_init_wait_queue(&xprt->sending, "xprt_sending");
rpc_init_wait_queue(&xprt->resend, "xprt_resend"); rpc_init_wait_queue(&xprt->resend, "xprt_resend");
@ -941,41 +953,43 @@ static struct rpc_xprt *xprt_setup(int proto, struct sockaddr_in *ap, struct rpc
dprintk("RPC: created transport %p with %u slots\n", xprt, dprintk("RPC: created transport %p with %u slots\n", xprt,
xprt->max_reqs); xprt->max_reqs);
return xprt;
}
/**
* xprt_create_proto - create an RPC client transport
* @proto: requested transport protocol
* @sap: remote peer's address
* @to: timeout parameters for new transport
*
*/
struct rpc_xprt *xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to)
{
struct rpc_xprt *xprt;
xprt = xprt_setup(proto, sap, to);
if (IS_ERR(xprt))
dprintk("RPC: xprt_create_proto failed\n");
else
dprintk("RPC: xprt_create_proto created xprt %p\n", xprt);
return xprt; return xprt;
} }
/** /**
* xprt_destroy - destroy an RPC transport, killing off all requests. * xprt_destroy - destroy an RPC transport, killing off all requests.
* @xprt: transport to destroy * @kref: kref for the transport to destroy
* *
*/ */
int xprt_destroy(struct rpc_xprt *xprt) static void xprt_destroy(struct kref *kref)
{ {
struct rpc_xprt *xprt = container_of(kref, struct rpc_xprt, kref);
dprintk("RPC: destroying transport %p\n", xprt); dprintk("RPC: destroying transport %p\n", xprt);
xprt->shutdown = 1; xprt->shutdown = 1;
del_timer_sync(&xprt->timer); del_timer_sync(&xprt->timer);
xprt->ops->destroy(xprt); xprt->ops->destroy(xprt);
kfree(xprt); kfree(xprt);
}
return 0;
/**
* xprt_put - release a reference to an RPC transport.
* @xprt: pointer to the transport
*
*/
void xprt_put(struct rpc_xprt *xprt)
{
kref_put(&xprt->kref, xprt_destroy);
}
/**
* xprt_get - return a reference to an RPC transport.
* @xprt: pointer to the transport
*
*/
struct rpc_xprt *xprt_get(struct rpc_xprt *xprt)
{
kref_get(&xprt->kref);
return xprt;
} }

View File

@ -125,6 +125,47 @@ static inline void xs_pktdump(char *msg, u32 *packet, unsigned int count)
} }
#endif #endif
static void xs_format_peer_addresses(struct rpc_xprt *xprt)
{
struct sockaddr_in *addr = (struct sockaddr_in *) &xprt->addr;
char *buf;
buf = kzalloc(20, GFP_KERNEL);
if (buf) {
snprintf(buf, 20, "%u.%u.%u.%u",
NIPQUAD(addr->sin_addr.s_addr));
}
xprt->address_strings[RPC_DISPLAY_ADDR] = buf;
buf = kzalloc(8, GFP_KERNEL);
if (buf) {
snprintf(buf, 8, "%u",
ntohs(addr->sin_port));
}
xprt->address_strings[RPC_DISPLAY_PORT] = buf;
if (xprt->prot == IPPROTO_UDP)
xprt->address_strings[RPC_DISPLAY_PROTO] = "udp";
else
xprt->address_strings[RPC_DISPLAY_PROTO] = "tcp";
buf = kzalloc(48, GFP_KERNEL);
if (buf) {
snprintf(buf, 48, "addr=%u.%u.%u.%u port=%u proto=%s",
NIPQUAD(addr->sin_addr.s_addr),
ntohs(addr->sin_port),
xprt->prot == IPPROTO_UDP ? "udp" : "tcp");
}
xprt->address_strings[RPC_DISPLAY_ALL] = buf;
}
static void xs_free_peer_addresses(struct rpc_xprt *xprt)
{
kfree(xprt->address_strings[RPC_DISPLAY_ADDR]);
kfree(xprt->address_strings[RPC_DISPLAY_PORT]);
kfree(xprt->address_strings[RPC_DISPLAY_ALL]);
}
#define XS_SENDMSG_FLAGS (MSG_DONTWAIT | MSG_NOSIGNAL) #define XS_SENDMSG_FLAGS (MSG_DONTWAIT | MSG_NOSIGNAL)
static inline int xs_send_head(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base, unsigned int len) static inline int xs_send_head(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base, unsigned int len)
@ -295,7 +336,7 @@ static int xs_udp_send_request(struct rpc_task *task)
req->rq_xtime = jiffies; req->rq_xtime = jiffies;
status = xs_sendpages(xprt->sock, (struct sockaddr *) &xprt->addr, status = xs_sendpages(xprt->sock, (struct sockaddr *) &xprt->addr,
sizeof(xprt->addr), xdr, req->rq_bytes_sent); xprt->addrlen, xdr, req->rq_bytes_sent);
dprintk("RPC: xs_udp_send_request(%u) = %d\n", dprintk("RPC: xs_udp_send_request(%u) = %d\n",
xdr->len - req->rq_bytes_sent, status); xdr->len - req->rq_bytes_sent, status);
@ -485,6 +526,7 @@ static void xs_destroy(struct rpc_xprt *xprt)
xprt_disconnect(xprt); xprt_disconnect(xprt);
xs_close(xprt); xs_close(xprt);
xs_free_peer_addresses(xprt);
kfree(xprt->slot); kfree(xprt->slot);
} }
@ -959,6 +1001,19 @@ static unsigned short xs_get_random_port(void)
return rand + xprt_min_resvport; return rand + xprt_min_resvport;
} }
/**
* xs_print_peer_address - format an IPv4 address for printing
* @xprt: generic transport
* @format: flags field indicating which parts of the address to render
*/
static char *xs_print_peer_address(struct rpc_xprt *xprt, enum rpc_display_format_t format)
{
if (xprt->address_strings[format] != NULL)
return xprt->address_strings[format];
else
return "unprintable";
}
/** /**
* xs_set_port - reset the port number in the remote endpoint address * xs_set_port - reset the port number in the remote endpoint address
* @xprt: generic transport * @xprt: generic transport
@ -967,8 +1022,11 @@ static unsigned short xs_get_random_port(void)
*/ */
static void xs_set_port(struct rpc_xprt *xprt, unsigned short port) static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)
{ {
struct sockaddr_in *sap = (struct sockaddr_in *) &xprt->addr;
dprintk("RPC: setting port for xprt %p to %u\n", xprt, port); dprintk("RPC: setting port for xprt %p to %u\n", xprt, port);
xprt->addr.sin_port = htons(port);
sap->sin_port = htons(port);
} }
static int xs_bindresvport(struct rpc_xprt *xprt, struct socket *sock) static int xs_bindresvport(struct rpc_xprt *xprt, struct socket *sock)
@ -1011,11 +1069,9 @@ static void xs_udp_connect_worker(void *args)
struct socket *sock = xprt->sock; struct socket *sock = xprt->sock;
int err, status = -EIO; int err, status = -EIO;
if (xprt->shutdown || xprt->addr.sin_port == 0) if (xprt->shutdown || !xprt_bound(xprt))
goto out; goto out;
dprintk("RPC: xs_udp_connect_worker for xprt %p\n", xprt);
/* Start by resetting any existing state */ /* Start by resetting any existing state */
xs_close(xprt); xs_close(xprt);
@ -1029,6 +1085,9 @@ static void xs_udp_connect_worker(void *args)
goto out; goto out;
} }
dprintk("RPC: worker connecting xprt %p to address: %s\n",
xprt, xs_print_peer_address(xprt, RPC_DISPLAY_ALL));
if (!xprt->inet) { if (!xprt->inet) {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
@ -1094,11 +1153,9 @@ static void xs_tcp_connect_worker(void *args)
struct socket *sock = xprt->sock; struct socket *sock = xprt->sock;
int err, status = -EIO; int err, status = -EIO;
if (xprt->shutdown || xprt->addr.sin_port == 0) if (xprt->shutdown || !xprt_bound(xprt))
goto out; goto out;
dprintk("RPC: xs_tcp_connect_worker for xprt %p\n", xprt);
if (!xprt->sock) { if (!xprt->sock) {
/* start from scratch */ /* start from scratch */
if ((err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock)) < 0) { if ((err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock)) < 0) {
@ -1114,6 +1171,9 @@ static void xs_tcp_connect_worker(void *args)
/* "close" the socket, preserving the local port */ /* "close" the socket, preserving the local port */
xs_tcp_reuse_connection(xprt); xs_tcp_reuse_connection(xprt);
dprintk("RPC: worker connecting xprt %p to address: %s\n",
xprt, xs_print_peer_address(xprt, RPC_DISPLAY_ALL));
if (!xprt->inet) { if (!xprt->inet) {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
@ -1147,7 +1207,7 @@ static void xs_tcp_connect_worker(void *args)
xprt->stat.connect_count++; xprt->stat.connect_count++;
xprt->stat.connect_start = jiffies; xprt->stat.connect_start = jiffies;
status = kernel_connect(sock, (struct sockaddr *) &xprt->addr, status = kernel_connect(sock, (struct sockaddr *) &xprt->addr,
sizeof(xprt->addr), O_NONBLOCK); xprt->addrlen, O_NONBLOCK);
dprintk("RPC: %p connect status %d connected %d sock state %d\n", dprintk("RPC: %p connect status %d connected %d sock state %d\n",
xprt, -status, xprt_connected(xprt), sock->sk->sk_state); xprt, -status, xprt_connected(xprt), sock->sk->sk_state);
if (status < 0) { if (status < 0) {
@ -1255,8 +1315,10 @@ static void xs_tcp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
static struct rpc_xprt_ops xs_udp_ops = { static struct rpc_xprt_ops xs_udp_ops = {
.set_buffer_size = xs_udp_set_buffer_size, .set_buffer_size = xs_udp_set_buffer_size,
.print_addr = xs_print_peer_address,
.reserve_xprt = xprt_reserve_xprt_cong, .reserve_xprt = xprt_reserve_xprt_cong,
.release_xprt = xprt_release_xprt_cong, .release_xprt = xprt_release_xprt_cong,
.rpcbind = rpc_getport,
.set_port = xs_set_port, .set_port = xs_set_port,
.connect = xs_connect, .connect = xs_connect,
.buf_alloc = rpc_malloc, .buf_alloc = rpc_malloc,
@ -1271,8 +1333,10 @@ static struct rpc_xprt_ops xs_udp_ops = {
}; };
static struct rpc_xprt_ops xs_tcp_ops = { static struct rpc_xprt_ops xs_tcp_ops = {
.print_addr = xs_print_peer_address,
.reserve_xprt = xprt_reserve_xprt, .reserve_xprt = xprt_reserve_xprt,
.release_xprt = xs_tcp_release_xprt, .release_xprt = xs_tcp_release_xprt,
.rpcbind = rpc_getport,
.set_port = xs_set_port, .set_port = xs_set_port,
.connect = xs_connect, .connect = xs_connect,
.buf_alloc = rpc_malloc, .buf_alloc = rpc_malloc,
@ -1293,8 +1357,7 @@ static struct rpc_xprt_ops xs_tcp_ops = {
int xs_setup_udp(struct rpc_xprt *xprt, struct rpc_timeout *to) int xs_setup_udp(struct rpc_xprt *xprt, struct rpc_timeout *to)
{ {
size_t slot_table_size; size_t slot_table_size;
struct sockaddr_in *addr = (struct sockaddr_in *) &xprt->addr;
dprintk("RPC: setting up udp-ipv4 transport...\n");
xprt->max_reqs = xprt_udp_slot_table_entries; xprt->max_reqs = xprt_udp_slot_table_entries;
slot_table_size = xprt->max_reqs * sizeof(xprt->slot[0]); slot_table_size = xprt->max_reqs * sizeof(xprt->slot[0]);
@ -1302,10 +1365,12 @@ int xs_setup_udp(struct rpc_xprt *xprt, struct rpc_timeout *to)
if (xprt->slot == NULL) if (xprt->slot == NULL)
return -ENOMEM; return -ENOMEM;
xprt->prot = IPPROTO_UDP; if (ntohs(addr->sin_port != 0))
xprt_set_bound(xprt);
xprt->port = xs_get_random_port(); xprt->port = xs_get_random_port();
xprt->prot = IPPROTO_UDP;
xprt->tsh_size = 0; xprt->tsh_size = 0;
xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0;
/* XXX: header size can vary due to auth type, IPv6, etc. */ /* XXX: header size can vary due to auth type, IPv6, etc. */
xprt->max_payload = (1U << 16) - (MAX_HEADER << 3); xprt->max_payload = (1U << 16) - (MAX_HEADER << 3);
@ -1322,6 +1387,10 @@ int xs_setup_udp(struct rpc_xprt *xprt, struct rpc_timeout *to)
else else
xprt_set_timeout(&xprt->timeout, 5, 5 * HZ); xprt_set_timeout(&xprt->timeout, 5, 5 * HZ);
xs_format_peer_addresses(xprt);
dprintk("RPC: set up transport to address %s\n",
xs_print_peer_address(xprt, RPC_DISPLAY_ALL));
return 0; return 0;
} }
@ -1334,8 +1403,7 @@ int xs_setup_udp(struct rpc_xprt *xprt, struct rpc_timeout *to)
int xs_setup_tcp(struct rpc_xprt *xprt, struct rpc_timeout *to) int xs_setup_tcp(struct rpc_xprt *xprt, struct rpc_timeout *to)
{ {
size_t slot_table_size; size_t slot_table_size;
struct sockaddr_in *addr = (struct sockaddr_in *) &xprt->addr;
dprintk("RPC: setting up tcp-ipv4 transport...\n");
xprt->max_reqs = xprt_tcp_slot_table_entries; xprt->max_reqs = xprt_tcp_slot_table_entries;
slot_table_size = xprt->max_reqs * sizeof(xprt->slot[0]); slot_table_size = xprt->max_reqs * sizeof(xprt->slot[0]);
@ -1343,10 +1411,12 @@ int xs_setup_tcp(struct rpc_xprt *xprt, struct rpc_timeout *to)
if (xprt->slot == NULL) if (xprt->slot == NULL)
return -ENOMEM; return -ENOMEM;
xprt->prot = IPPROTO_TCP; if (ntohs(addr->sin_port) != 0)
xprt_set_bound(xprt);
xprt->port = xs_get_random_port(); xprt->port = xs_get_random_port();
xprt->prot = IPPROTO_TCP;
xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32); xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32);
xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0;
xprt->max_payload = RPC_MAX_FRAGMENT_SIZE; xprt->max_payload = RPC_MAX_FRAGMENT_SIZE;
INIT_WORK(&xprt->connect_worker, xs_tcp_connect_worker, xprt); INIT_WORK(&xprt->connect_worker, xs_tcp_connect_worker, xprt);
@ -1362,5 +1432,9 @@ int xs_setup_tcp(struct rpc_xprt *xprt, struct rpc_timeout *to)
else else
xprt_set_timeout(&xprt->timeout, 2, 60 * HZ); xprt_set_timeout(&xprt->timeout, 2, 60 * HZ);
xs_format_peer_addresses(xprt);
dprintk("RPC: set up transport to address %s\n",
xs_print_peer_address(xprt, RPC_DISPLAY_ALL));
return 0; return 0;
} }