Merge git://git.linux-nfs.org/pub/linux/nfs-2.6
authorLinus Torvalds <torvalds@g5.osdl.org>
Sat, 23 Sep 2006 23:58:40 +0000 (16:58 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Sat, 23 Sep 2006 23:58:40 +0000 (16:58 -0700)
* 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

56 files changed:
block/ll_rw_blk.c
fs/Kconfig
fs/dcache.c
fs/lockd/clntproc.c
fs/lockd/host.c
fs/lockd/mon.c
fs/nfs/Makefile
fs/nfs/callback.c
fs/nfs/callback.h
fs/nfs/callback_proc.c
fs/nfs/client.c [new file with mode: 0644]
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/dir.c
fs/nfs/file.c
fs/nfs/getroot.c [new file with mode: 0644]
fs/nfs/idmap.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/mount_clnt.c
fs/nfs/namespace.c
fs/nfs/nfs2xdr.c
fs/nfs/nfs3proc.c
fs/nfs/nfs3xdr.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4namespace.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4renewd.c
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
fs/nfs/proc.c
fs/nfs/read.c
fs/nfs/super.c
fs/nfs/write.c
fs/nfsd/nfs4callback.c
include/linux/blkdev.h
include/linux/dcache.h
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h
include/linux/nfs_idmap.h
include/linux/nfs_xdr.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/rpc_pipe_fs.h
include/linux/sunrpc/sched.h
include/linux/sunrpc/xprt.h
include/linux/writeback.h
mm/page-writeback.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/clnt.c
net/sunrpc/pmap_clnt.c
net/sunrpc/rpc_pipe.c
net/sunrpc/sched.c
net/sunrpc/sunrpc_syms.c
net/sunrpc/timer.c
net/sunrpc/xprt.c
net/sunrpc/xprtsock.c

index ddd9253..dcbd6ff 100644 (file)
@@ -2734,6 +2734,18 @@ long blk_congestion_wait(int rw, long timeout)
 
 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
  */
index 5305816..a270026 100644 (file)
@@ -1471,8 +1471,8 @@ config NFS_V4
          If unsure, say N.
 
 config NFS_DIRECTIO
-       bool "Allow direct I/O on NFS files (EXPERIMENTAL)"
-       depends on NFS_FS && EXPERIMENTAL
+       bool "Allow direct I/O on NFS files"
+       depends on NFS_FS
        help
          This option enables applications to perform uncached I/O on files
          in NFS file systems using the O_DIRECT open() flag.  When O_DIRECT
index 1b4a3a3..17b392a 100644 (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
  * 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;
        int len = entry->d_name.len;
        const char *name = entry->d_name.name;
        unsigned int hash = entry->d_name.hash;
 
-       BUG_ON(!list_empty(&entry->d_alias));
-       spin_lock(&dcache_lock);
-       if (!inode)
-               goto do_negative;
+       if (!inode) {
+               entry->d_inode = NULL;
+               return NULL;
+       }
+
        list_for_each_entry(alias, &inode->i_dentry, d_alias) {
                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))
                        continue;
                dget_locked(alias);
-               spin_unlock(&dcache_lock);
-               BUG_ON(!d_unhashed(alias));
-               iput(inode);
                return alias;
        }
+
        list_add(&entry->d_alias, &inode->i_dentry);
-do_negative:
        entry->d_inode = inode;
        fsnotify_d_instantiate(entry, inode);
-       spin_unlock(&dcache_lock);
-       security_d_instantiate(entry, inode);
        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);
 
 /**
@@ -1235,6 +1253,11 @@ static void __d_rehash(struct dentry * entry, struct hlist_head *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
  * @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)
 {
-       struct hlist_head *list = d_hash(entry->d_parent, entry->d_name.hash);
-
        spin_lock(&dcache_lock);
        spin_lock(&entry->d_lock);
-       __d_rehash(entry, list);
+       _d_rehash(entry);
        spin_unlock(&entry->d_lock);
        spin_unlock(&dcache_lock);
 }
@@ -1386,6 +1407,120 @@ already_unhashed:
        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
  * @dentry: dentry to report
@@ -1784,6 +1919,7 @@ EXPORT_SYMBOL(d_instantiate);
 EXPORT_SYMBOL(d_invalidate);
 EXPORT_SYMBOL(d_lookup);
 EXPORT_SYMBOL(d_move);
+EXPORT_SYMBOL_GPL(d_materialise_unique);
 EXPORT_SYMBOL(d_path);
 EXPORT_SYMBOL(d_prune_aliases);
 EXPORT_SYMBOL(d_rehash);
index 89ba0df..50dbb67 100644 (file)
@@ -151,11 +151,13 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req)
 int
 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_rqst         *call;
        sigset_t                oldset;
        unsigned long           flags;
-       int                     status, proto, vers;
+       int                     status, vers;
 
        vers = (NFS_PROTO(inode)->version == 3) ? 4 : 1;
        if (NFS_PROTO(inode)->version > 3) {
@@ -163,10 +165,8 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
                return -ENOLCK;
        }
 
-       /* Retrieve transport protocol from NFS client */
-       proto = NFS_CLIENT(inode)->cl_xprt->prot;
-
-       host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers);
+       rpc_peeraddr(client, (struct sockaddr *) &addr, sizeof(addr));
+       host = nlmclnt_lookup_host(&addr, client->cl_xprt->prot, vers);
        if (host == NULL)
                return -ENOLCK;
 
index 38b0e8a..703fb03 100644 (file)
@@ -26,7 +26,6 @@
 #define NLM_HOST_REBIND                (60 * 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_ADDR(sv)      (&(sv)->s_nlmclnt->cl_xprt->addr)
 
 static struct nlm_host *       nlm_hosts[NLM_HOST_NRHASH];
 static unsigned long           next_gc;
@@ -167,7 +166,6 @@ struct rpc_clnt *
 nlm_bind_host(struct nlm_host *host)
 {
        struct rpc_clnt *clnt;
-       struct rpc_xprt *xprt;
 
        dprintk("lockd: nlm_bind_host(%08x)\n",
                        (unsigned)ntohl(host->h_addr.sin_addr.s_addr));
@@ -179,7 +177,6 @@ nlm_bind_host(struct nlm_host *host)
         * RPC rebind is required
         */
        if ((clnt = host->h_rpcclnt) != NULL) {
-               xprt = clnt->cl_xprt;
                if (time_after_eq(jiffies, host->h_nextrebind)) {
                        rpc_force_rebind(clnt);
                        host->h_nextrebind = jiffies + NLM_HOST_REBIND;
@@ -187,31 +184,37 @@ nlm_bind_host(struct nlm_host *host)
                                        host->h_nextrebind - jiffies);
                }
        } else {
-               xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL);
-               if (IS_ERR(xprt))
-                       goto forgetit;
-
-               xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout);
-               xprt->resvport = 1;     /* NLM requires a reserved port */
-
-               /* Existing NLM servers accept AUTH_UNIX only */
-               clnt = rpc_new_client(xprt, host->h_name, &nlm_program,
-                                       host->h_version, RPC_AUTH_UNIX);
-               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;
+               unsigned long increment = nlmsvc_timeout * HZ;
+               struct rpc_timeout timeparms = {
+                       .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),
+               };
+
+               clnt = rpc_create(&args);
+               if (!IS_ERR(clnt))
+                       host->h_rpcclnt = clnt;
+               else {
+                       printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
+                       clnt = NULL;
+               }
        }
 
        mutex_unlock(&host->h_mutex);
        return clnt;
-
-forgetit:
-       printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
-       mutex_unlock(&host->h_mutex);
-       return NULL;
 }
 
 /*
index 3fc683f..5954dcb 100644 (file)
@@ -109,30 +109,23 @@ nsm_unmonitor(struct nlm_host *host)
 static struct rpc_clnt *
 nsm_create(void)
 {
-       struct rpc_xprt         *xprt;
-       struct rpc_clnt         *clnt;
-       struct sockaddr_in      sin;
-
-       sin.sin_family = AF_INET;
-       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;
+       struct sockaddr_in      sin = {
+               .sin_family     = AF_INET,
+               .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),
+       };
+
+       return rpc_create(&args);
 }
 
 /*
index 0b572a0..f4580b4 100644 (file)
@@ -4,9 +4,9 @@
 
 obj-$(CONFIG_NFS_FS) += nfs.o
 
-nfs-y                  := dir.o file.o inode.o super.o nfs2xdr.o pagelist.o \
-                          proc.o read.o symlink.o unlink.o write.o \
-                          namespace.o
+nfs-y                  := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \
+                          pagelist.o proc.o read.o symlink.o unlink.o \
+                          write.o namespace.o
 nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o      
 nfs-$(CONFIG_NFS_V3)   += nfs3proc.o nfs3xdr.o
 nfs-$(CONFIG_NFS_V3_ACL)       += nfs3acl.o
index fe0a6b8..a3ee113 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "nfs4_fs.h"
 #include "callback.h"
+#include "internal.h"
 
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
 
@@ -36,6 +37,21 @@ static struct svc_program nfs4_callback_program;
 
 unsigned int nfs_callback_set_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.
@@ -134,10 +150,8 @@ out_err:
 /*
  * 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();
        mutex_lock(&nfs_callback_mutex);
        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);
        mutex_unlock(&nfs_callback_mutex);
        unlock_kernel();
-       return ret;
 }
 
 static int nfs_callback_authenticate(struct svc_rqst *rqstp)
 {
-       struct in_addr *addr = &rqstp->rq_addr.sin_addr;
-       struct nfs4_client *clp;
+       struct sockaddr_in *addr = &rqstp->rq_addr;
+       struct nfs_client *clp;
 
        /* Don't talk to strangers */
-       clp = nfs4_find_client(addr);
+       clp = nfs_find_client(addr, 4);
        if (clp == NULL)
                return SVC_DROP;
-       dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr));
-       nfs4_put_client(clp);
+       dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr->sin_addr));
+       nfs_put_client(clp);
        switch (rqstp->rq_authop->flavour) {
                case RPC_AUTH_NULL:
                        if (rqstp->rq_proc != CB_NULL)
index b252e7f..5676163 100644 (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_recall(struct cb_recallargs *args, void *dummy);
 
+#ifdef CONFIG_NFS_V4
 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 short nfs_callback_tcpport;
index 7719483..97cf8f7 100644 (file)
 #include "nfs4_fs.h"
 #include "callback.h"
 #include "delegation.h"
+#include "internal.h"
 
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
  
 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_inode *nfsi;
        struct inode *inode;
        
        res->bitmap[0] = res->bitmap[1] = 0;
        res->status = htonl(NFS4ERR_BADHANDLE);
-       clp = nfs4_find_client(&args->addr->sin_addr);
+       clp = nfs_find_client(args->addr, 4);
        if (clp == NULL)
                goto out;
        inode = nfs_delegation_find_inode(clp, &args->fh);
@@ -48,7 +49,7 @@ out_iput:
        up_read(&nfsi->rwsem);
        iput(inode);
 out_putclient:
-       nfs4_put_client(clp);
+       nfs_put_client(clp);
 out:
        dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res->status));
        return res->status;
@@ -56,12 +57,12 @@ out:
 
 unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
 {
-       struct nfs4_client *clp;
+       struct nfs_client *clp;
        struct inode *inode;
        unsigned res;
        
        res = htonl(NFS4ERR_BADHANDLE);
-       clp = nfs4_find_client(&args->addr->sin_addr);
+       clp = nfs_find_client(args->addr, 4);
        if (clp == NULL)
                goto out;
        inode = nfs_delegation_find_inode(clp, &args->fh);
@@ -80,7 +81,7 @@ unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
        }
        iput(inode);
 out_putclient:
-       nfs4_put_client(clp);
+       nfs_put_client(clp);
 out:
        dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res));
        return res;
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
new file mode 100644 (file)
index 0000000..ec1938d
--- /dev/null
@@ -0,0 +1,1448 @@
+/* client.c: NFS client sharing and management code
+ *
+ * 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/sunrpc/metrics.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/inet.h>
+#include <linux/nfs_xdr.h>
+
+#include <asm/system.h>
+
+#include "nfs4_fs.h"
+#include "callback.h"
+#include "delegation.h"
+#include "iostat.h"
+#include "internal.h"
+
+#define NFSDBG_FACILITY                NFSDBG_CLIENT
+
+static DEFINE_SPINLOCK(nfs_client_lock);
+static LIST_HEAD(nfs_client_list);
+static LIST_HEAD(nfs_volume_list);
+static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
+
+/*
+ * RPC cruft for NFS
+ */
+static struct rpc_version *nfs_version[5] = {
+       [2]                     = &nfs_version2,
+#ifdef CONFIG_NFS_V3
+       [3]                     = &nfs_version3,
+#endif
+#ifdef CONFIG_NFS_V4
+       [4]                     = &nfs_version4,
+#endif
+};
+
+struct rpc_program nfs_program = {
+       .name                   = "nfs",
+       .number                 = NFS_PROGRAM,
+       .nrvers                 = ARRAY_SIZE(nfs_version),
+       .version                = nfs_version,
+       .stats                  = &nfs_rpcstat,
+       .pipe_dir_name          = "/nfs",
+};
+
+struct rpc_stat nfs_rpcstat = {
+       .program                = &nfs_program
+};
+
+
+#ifdef CONFIG_NFS_V3_ACL
+static struct rpc_stat         nfsacl_rpcstat = { &nfsacl_program };
+static struct rpc_version *    nfsacl_version[] = {
+       [3]                     = &nfsacl_version3,
+};
+
+struct rpc_program             nfsacl_program = {
+       .name                   = "nfsacl",
+       .number                 = NFS_ACL_PROGRAM,
+       .nrvers                 = ARRAY_SIZE(nfsacl_version),
+       .version                = nfsacl_version,
+       .stats                  = &nfsacl_rpcstat,
+};
+#endif  /* CONFIG_NFS_V3_ACL */
+
+/*
+ * Allocate a shared client record
+ *
+ * Since these are allocated/deallocated very rarely, we don't
+ * bother putting them in a slab cache...
+ */
+static struct nfs_client *nfs_alloc_client(const char *hostname,
+                                          const struct sockaddr_in *addr,
+                                          int nfsversion)
+{
+       struct nfs_client *clp;
+       int error;
+
+       if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
+               goto error_0;
+
+       error = rpciod_up();
+       if (error < 0) {
+               dprintk("%s: couldn't start rpciod! Error = %d\n",
+                               __FUNCTION__, error);
+               goto error_1;
+       }
+       __set_bit(NFS_CS_RPCIOD, &clp->cl_res_state);
+
+       if (nfsversion == 4) {
+               if (nfs_callback_up() < 0)
+                       goto error_2;
+               __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
+       }
+
+       atomic_set(&clp->cl_count, 1);
+       clp->cl_cons_state = NFS_CS_INITING;
+
+       clp->cl_nfsversion = nfsversion;
+       memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
+
+       if (hostname) {
+               clp->cl_hostname = kstrdup(hostname, GFP_KERNEL);
+               if (!clp->cl_hostname)
+                       goto error_3;
+       }
+
+       INIT_LIST_HEAD(&clp->cl_superblocks);
+       clp->cl_rpcclient = ERR_PTR(-EINVAL);
+
+#ifdef CONFIG_NFS_V4
+       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);
+       INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
+       rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
+       clp->cl_boot_time = CURRENT_TIME;
+       clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
+#endif
+
+       return clp;
+
+error_3:
+       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
+               nfs_callback_down();
+error_2:
+       rpciod_down();
+       __clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state);
+error_1:
+       kfree(clp);
+error_0:
+       return NULL;
+}
+
+static void nfs4_shutdown_client(struct nfs_client *clp)
+{
+#ifdef CONFIG_NFS_V4
+       if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
+               nfs4_kill_renewd(clp);
+       while (!list_empty(&clp->cl_unused)) {
+               struct nfs4_state_owner *sp;
+
+               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));
+       if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
+               nfs_idmap_delete(clp);
+#endif
+}
+
+/*
+ * Destroy a shared client record
+ */
+static void nfs_free_client(struct nfs_client *clp)
+{
+       dprintk("--> nfs_free_client(%d)\n", clp->cl_nfsversion);
+
+       nfs4_shutdown_client(clp);
+
+       /* -EIO all pending I/O */
+       if (!IS_ERR(clp->cl_rpcclient))
+               rpc_shutdown_client(clp->cl_rpcclient);
+
+       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
+               nfs_callback_down();
+
+       if (__test_and_clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state))
+               rpciod_down();
+
+       kfree(clp->cl_hostname);
+       kfree(clp);
+
+       dprintk("<-- nfs_free_client()\n");
+}
+
+/*
+ * Release a reference to a shared client record
+ */
+void nfs_put_client(struct nfs_client *clp)
+{
+       if (!clp)
+               return;
+
+       dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count));
+
+       if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) {
+               list_del(&clp->cl_share_link);
+               spin_unlock(&nfs_client_lock);
+
+               BUG_ON(!list_empty(&clp->cl_superblocks));
+
+               nfs_free_client(clp);
+       }
+}
+
+/*
+ * Find a client by address
+ * - caller must hold nfs_client_lock
+ */
+static struct nfs_client *__nfs_find_client(const struct sockaddr_in *addr, int nfsversion)
+{
+       struct nfs_client *clp;
+
+       list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
+               /* Different NFS versions cannot share the same nfs_client */
+               if (clp->cl_nfsversion != nfsversion)
+                       continue;
+
+               if (memcmp(&clp->cl_addr.sin_addr, &addr->sin_addr,
+                          sizeof(clp->cl_addr.sin_addr)) != 0)
+                       continue;
+
+               if (clp->cl_addr.sin_port == addr->sin_port)
+                       goto found;
+       }
+
+       return NULL;
+
+found:
+       atomic_inc(&clp->cl_count);
+       return clp;
+}
+
+/*
+ * Find a client by IP address and protocol version
+ * - returns NULL if no such client
+ */
+struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversion)
+{
+       struct nfs_client *clp;
+
+       spin_lock(&nfs_client_lock);
+       clp = __nfs_find_client(addr, nfsversion);
+       spin_unlock(&nfs_client_lock);
+
+       BUG_ON(clp && clp->cl_cons_state == 0);
+
+       return clp;
+}
+
+/*
+ * Look up a client by IP address and protocol version
+ * - creates a new record if one doesn't yet exist
+ */
+static struct nfs_client *nfs_get_client(const char *hostname,
+                                        const struct sockaddr_in *addr,
+                                        int nfsversion)
+{
+       struct nfs_client *clp, *new = NULL;
+       int error;
+
+       dprintk("--> nfs_get_client(%s,"NIPQUAD_FMT":%d,%d)\n",
+               hostname ?: "", NIPQUAD(addr->sin_addr),
+               addr->sin_port, nfsversion);
+
+       /* see if the client already exists */
+       do {
+               spin_lock(&nfs_client_lock);
+
+               clp = __nfs_find_client(addr, nfsversion);
+               if (clp)
+                       goto found_client;
+               if (new)
+                       goto install_client;
+
+               spin_unlock(&nfs_client_lock);
+
+               new = nfs_alloc_client(hostname, addr, nfsversion);
+       } while (new);
+
+       return ERR_PTR(-ENOMEM);
+
+       /* install a new client and return with it unready */
+install_client:
+       clp = new;
+       list_add(&clp->cl_share_link, &nfs_client_list);
+       spin_unlock(&nfs_client_lock);
+       dprintk("--> nfs_get_client() = %p [new]\n", clp);
+       return clp;
+
+       /* found an existing client
+        * - make sure it's ready before returning
+        */
+found_client:
+       spin_unlock(&nfs_client_lock);
+
+       if (new)
+               nfs_free_client(new);
+
+       if (clp->cl_cons_state == NFS_CS_INITING) {
+               DECLARE_WAITQUEUE(myself, current);
+
+               add_wait_queue(&nfs_client_active_wq, &myself);
+
+               for (;;) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       if (signal_pending(current) ||
+                           clp->cl_cons_state > NFS_CS_READY)
+                               break;
+                       schedule();
+               }
+
+               remove_wait_queue(&nfs_client_active_wq, &myself);
+
+               if (signal_pending(current)) {
+                       nfs_put_client(clp);
+                       return ERR_PTR(-ERESTARTSYS);
+               }
+       }
+
+       if (clp->cl_cons_state < NFS_CS_READY) {
+               error = clp->cl_cons_state;
+               nfs_put_client(clp);
+               return ERR_PTR(error);
+       }
+
+       BUG_ON(clp->cl_cons_state != NFS_CS_READY);
+
+       dprintk("--> nfs_get_client() = %p [share]\n", clp);
+       return clp;
+}
+
+/*
+ * Mark a server as ready or failed
+ */
+static void nfs_mark_client_ready(struct nfs_client *clp, int state)
+{
+       clp->cl_cons_state = state;
+       wake_up_all(&nfs_client_active_wq);
+}
+
+/*
+ * Initialise the timeout values for a connection
+ */
+static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
+                                   unsigned int timeo, unsigned int retrans)
+{
+       to->to_initval = timeo * HZ / 10;
+       to->to_retries = retrans;
+       if (!to->to_retries)
+               to->to_retries = 2;
+
+       switch (proto) {
+       case IPPROTO_TCP:
+               if (!to->to_initval)
+                       to->to_initval = 60 * HZ;
+               if (to->to_initval > NFS_MAX_TCP_TIMEOUT)
+                       to->to_initval = NFS_MAX_TCP_TIMEOUT;
+               to->to_increment = to->to_initval;
+               to->to_maxval = to->to_initval + (to->to_increment * to->to_retries);
+               to->to_exponential = 0;
+               break;
+       case IPPROTO_UDP:
+       default:
+               if (!to->to_initval)
+                       to->to_initval = 11 * HZ / 10;
+               if (to->to_initval > NFS_MAX_UDP_TIMEOUT)
+                       to->to_initval = NFS_MAX_UDP_TIMEOUT;
+               to->to_maxval = NFS_MAX_UDP_TIMEOUT;
+               to->to_exponential = 1;
+               break;
+       }
+}
+
+/*
+ * Create an RPC client handle
+ */
+static int nfs_create_rpc_client(struct nfs_client *clp, int proto,
+                                               unsigned int timeo,
+                                               unsigned int retrans,
+                                               rpc_authflavor_t flavor)
+{
+       struct rpc_timeout      timeparms;
+       struct rpc_clnt         *clnt = NULL;
+       struct rpc_create_args args = {
+               .protocol       = proto,
+               .address        = (struct sockaddr *)&clp->cl_addr,
+               .addrsize       = sizeof(clp->cl_addr),
+               .timeout        = &timeparms,
+               .servername     = clp->cl_hostname,
+               .program        = &nfs_program,
+               .version        = clp->rpc_ops->version,
+               .authflavor     = flavor,
+       };
+
+       if (!IS_ERR(clp->cl_rpcclient))
+               return 0;
+
+       nfs_init_timeout_values(&timeparms, proto, timeo, retrans);
+       clp->retrans_timeo = timeparms.to_initval;
+       clp->retrans_count = timeparms.to_retries;
+
+       clnt = rpc_create(&args);
+       if (IS_ERR(clnt)) {
+               dprintk("%s: cannot create RPC client. Error = %ld\n",
+                               __FUNCTION__, PTR_ERR(clnt));
+               return PTR_ERR(clnt);
+       }
+
+       clp->cl_rpcclient = clnt;
+       return 0;
+}
+
+/*
+ * Version 2 or 3 client destruction
+ */
+static void nfs_destroy_server(struct nfs_server *server)
+{
+       if (!IS_ERR(server->client_acl))
+               rpc_shutdown_client(server->client_acl);
+
+       if (!(server->flags & NFS_MOUNT_NONLM))
+               lockd_down();   /* release rpc.lockd */
+}
+
+/*
+ * Version 2 or 3 lockd setup
+ */
+static int nfs_start_lockd(struct nfs_server *server)
+{
+       int error = 0;
+
+       if (server->nfs_client->cl_nfsversion > 3)
+               goto out;
+       if (server->flags & NFS_MOUNT_NONLM)
+               goto out;
+       error = lockd_up();
+       if (error < 0)
+               server->flags |= NFS_MOUNT_NONLM;
+       else
+               server->destroy = nfs_destroy_server;
+out:
+       return error;
+}
+
+/*
+ * Initialise an NFSv3 ACL client connection
+ */
+#ifdef CONFIG_NFS_V3_ACL
+static void nfs_init_server_aclclient(struct nfs_server *server)
+{
+       if (server->nfs_client->cl_nfsversion != 3)
+               goto out_noacl;
+       if (server->flags & NFS_MOUNT_NOACL)
+               goto out_noacl;
+
+       server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3);
+       if (IS_ERR(server->client_acl))
+               goto out_noacl;
+
+       /* No errors! Assume that Sun nfsacls are supported */
+       server->caps |= NFS_CAP_ACLS;
+       return;
+
+out_noacl:
+       server->caps &= ~NFS_CAP_ACLS;
+}
+#else
+static inline void nfs_init_server_aclclient(struct nfs_server *server)
+{
+       server->flags &= ~NFS_MOUNT_NOACL;
+       server->caps &= ~NFS_CAP_ACLS;
+}
+#endif
+
+/*
+ * Create a general RPC client
+ */
+static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t pseudoflavour)
+{
+       struct nfs_client *clp = server->nfs_client;
+
+       server->client = rpc_clone_client(clp->cl_rpcclient);
+       if (IS_ERR(server->client)) {
+               dprintk("%s: couldn't create rpc_client!\n", __FUNCTION__);
+               return PTR_ERR(server->client);
+       }
+
+       if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) {
+               struct rpc_auth *auth;
+
+               auth = rpcauth_create(pseudoflavour, server->client);
+               if (IS_ERR(auth)) {
+                       dprintk("%s: couldn't create credcache!\n", __FUNCTION__);
+                       return PTR_ERR(auth);
+               }
+       }
+       server->client->cl_softrtry = 0;
+       if (server->flags & NFS_MOUNT_SOFT)
+               server->client->cl_softrtry = 1;
+
+       server->client->cl_intr = 0;
+       if (server->flags & NFS4_MOUNT_INTR)
+               server->client->cl_intr = 1;
+
+       return 0;
+}
+
+/*
+ * Initialise an NFS2 or NFS3 client
+ */
+static int nfs_init_client(struct nfs_client *clp, const struct nfs_mount_data *data)
+{
+       int proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
+       int error;
+
+       if (clp->cl_cons_state == NFS_CS_READY) {
+               /* the client is already initialised */
+               dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp);
+               return 0;
+       }
+
+       /* Check NFS protocol revision and initialize RPC op vector */
+       clp->rpc_ops = &nfs_v2_clientops;
+#ifdef CONFIG_NFS_V3
+       if (clp->cl_nfsversion == 3)
+               clp->rpc_ops = &nfs_v3_clientops;
+#endif
+       /*
+        * Create a client RPC handle for doing FSSTAT with UNIX auth only
+        * - RFC 2623, sec 2.3.2
+        */
+       error = nfs_create_rpc_client(clp, proto, data->timeo, data->retrans,
+                       RPC_AUTH_UNIX);
+       if (error < 0)
+               goto error;
+       nfs_mark_client_ready(clp, NFS_CS_READY);
+       return 0;
+
+error:
+       nfs_mark_client_ready(clp, error);
+       dprintk("<-- nfs_init_client() = xerror %d\n", error);
+       return error;
+}
+
+/*
+ * Create a version 2 or 3 client
+ */
+static int nfs_init_server(struct nfs_server *server, const struct nfs_mount_data *data)
+{
+       struct nfs_client *clp;
+       int error, nfsvers = 2;
+
+       dprintk("--> nfs_init_server()\n");
+
+#ifdef CONFIG_NFS_V3
+       if (data->flags & NFS_MOUNT_VER3)
+               nfsvers = 3;
+#endif
+
+       /* Allocate or find a client reference we can use */
+       clp = nfs_get_client(data->hostname, &data->addr, nfsvers);
+       if (IS_ERR(clp)) {
+               dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp));
+               return PTR_ERR(clp);
+       }
+
+       error = nfs_init_client(clp, data);
+       if (error < 0)
+               goto error;
+
+       server->nfs_client = clp;
+
+       /* Initialise the client representation from the mount data */
+       server->flags = data->flags & NFS_MOUNT_FLAGMASK;
+
+       if (data->rsize)
+               server->rsize = nfs_block_size(data->rsize, NULL);
+       if (data->wsize)
+               server->wsize = nfs_block_size(data->wsize, NULL);
+
+       server->acregmin = data->acregmin * HZ;
+       server->acregmax = data->acregmax * HZ;
+       server->acdirmin = data->acdirmin * HZ;
+       server->acdirmax = data->acdirmax * HZ;
+
+       /* Start lockd here, before we might error out */
+       error = nfs_start_lockd(server);
+       if (error < 0)
+               goto error;
+
+       error = nfs_init_server_rpcclient(server, data->pseudoflavor);
+       if (error < 0)
+               goto error;
+
+       server->namelen  = data->namlen;
+       /* Create a client RPC handle for the NFSv3 ACL management interface */
+       nfs_init_server_aclclient(server);
+       if (clp->cl_nfsversion == 3) {
+               if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
+                       server->namelen = NFS3_MAXNAMLEN;
+               server->caps |= NFS_CAP_READDIRPLUS;
+       } else {
+               if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
+                       server->namelen = NFS2_MAXNAMLEN;
+       }
+
+       dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp);
+       return 0;
+
+error:
+       server->nfs_client = NULL;
+       nfs_put_client(clp);
+       dprintk("<-- nfs_init_server() = xerror %d\n", error);
+       return error;
+}
+
+/*
+ * Load up the server record from information gained in an fsinfo record
+ */
+static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo)
+{
+       unsigned long max_rpc_payload;
+
+       /* Work out a lot of parameters */
+       if (server->rsize == 0)
+               server->rsize = nfs_block_size(fsinfo->rtpref, NULL);
+       if (server->wsize == 0)
+               server->wsize = nfs_block_size(fsinfo->wtpref, NULL);
+
+       if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax)
+               server->rsize = nfs_block_size(fsinfo->rtmax, NULL);
+       if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax)
+               server->wsize = nfs_block_size(fsinfo->wtmax, NULL);
+
+       max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL);
+       if (server->rsize > max_rpc_payload)
+               server->rsize = max_rpc_payload;
+       if (server->rsize > NFS_MAX_FILE_IO_SIZE)
+               server->rsize = NFS_MAX_FILE_IO_SIZE;
+       server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD;
+
+       if (server->wsize > max_rpc_payload)
+               server->wsize = max_rpc_payload;
+       if (server->wsize > NFS_MAX_FILE_IO_SIZE)
+               server->wsize = NFS_MAX_FILE_IO_SIZE;
+       server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL);
+
+       server->dtsize = nfs_block_size(fsinfo->dtpref, NULL);
+       if (server->dtsize > PAGE_CACHE_SIZE)
+               server->dtsize = PAGE_CACHE_SIZE;
+       if (server->dtsize > server->rsize)
+               server->dtsize = server->rsize;
+
+       if (server->flags & NFS_MOUNT_NOAC) {
+               server->acregmin = server->acregmax = 0;
+               server->acdirmin = server->acdirmax = 0;
+       }
+
+       server->maxfilesize = fsinfo->maxfilesize;
+
+       /* We're airborne Set socket buffersize */
+       rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
+}
+
+/*
+ * Probe filesystem information, including the FSID on v2/v3
+ */
+static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr)
+{
+       struct nfs_fsinfo fsinfo;
+       struct nfs_client *clp = server->nfs_client;
+       int error;
+
+       dprintk("--> nfs_probe_fsinfo()\n");
+
+       if (clp->rpc_ops->set_capabilities != NULL) {
+               error = clp->rpc_ops->set_capabilities(server, mntfh);
+               if (error < 0)
+                       goto out_error;
+       }
+
+       fsinfo.fattr = fattr;
+       nfs_fattr_init(fattr);
+       error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo);
+       if (error < 0)
+               goto out_error;
+
+       nfs_server_set_fsinfo(server, &fsinfo);
+
+       /* Get some general file system info */
+       if (server->namelen == 0) {
+               struct nfs_pathconf pathinfo;
+
+               pathinfo.fattr = fattr;
+               nfs_fattr_init(fattr);
+
+               if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0)
+                       server->namelen = pathinfo.max_namelen;
+       }
+
+       dprintk("<-- nfs_probe_fsinfo() = 0\n");
+       return 0;
+
+out_error:
+       dprintk("nfs_probe_fsinfo: error = %d\n", -error);
+       return error;
+}
+
+/*
+ * Copy useful information when duplicating a server record
+ */
+static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)
+{
+       target->flags = source->flags;
+       target->acregmin = source->acregmin;
+       target->acregmax = source->acregmax;
+       target->acdirmin = source->acdirmin;
+       target->acdirmax = source->acdirmax;
+       target->caps = source->caps;
+}
+
+/*
+ * Allocate and initialise a server record
+ */
+static struct nfs_server *nfs_alloc_server(void)
+{
+       struct nfs_server *server;
+
+       server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
+       if (!server)
+               return NULL;
+
+       server->client = server->client_acl = ERR_PTR(-EINVAL);
+
+       /* Zero out the NFS state stuff */
+       INIT_LIST_HEAD(&server->client_link);
+       INIT_LIST_HEAD(&server->master_link);
+
+       server->io_stats = nfs_alloc_iostats();
+       if (!server->io_stats) {
+               kfree(server);
+               return NULL;
+       }
+
+       return server;
+}
+
+/*
+ * Free up a server record
+ */
+void nfs_free_server(struct nfs_server *server)
+{
+       dprintk("--> nfs_free_server()\n");
+
+       spin_lock(&nfs_client_lock);
+       list_del(&server->client_link);
+       list_del(&server->master_link);
+       spin_unlock(&nfs_client_lock);
+
+       if (server->destroy != NULL)
+               server->destroy(server);
+       if (!IS_ERR(server->client))
+               rpc_shutdown_client(server->client);
+
+       nfs_put_client(server->nfs_client);
+
+       nfs_free_iostats(server->io_stats);
+       kfree(server);
+       nfs_release_automount_timer();
+       dprintk("<-- nfs_free_server()\n");
+}
+
+/*
+ * Create a version 2 or 3 volume record
+ * - keyed on server and FSID
+ */
+struct nfs_server *nfs_create_server(const struct nfs_mount_data *data,
+                                    struct nfs_fh *mntfh)
+{
+       struct nfs_server *server;
+       struct nfs_fattr fattr;
+       int error;
+
+       server = nfs_alloc_server();
+       if (!server)
+               return ERR_PTR(-ENOMEM);
+
+       /* Get a client representation */
+       error = nfs_init_server(server, data);
+       if (error < 0)
+               goto error;
+
+       BUG_ON(!server->nfs_client);
+       BUG_ON(!server->nfs_client->rpc_ops);
+       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
+
+       /* Probe the root fh to retrieve its FSID */
+       error = nfs_probe_fsinfo(server, mntfh, &fattr);
+       if (error < 0)
+               goto error;
+       if (!(fattr.valid & NFS_ATTR_FATTR)) {
+               error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr);
+               if (error < 0) {
+                       dprintk("nfs_create_server: getattr error = %d\n", -error);
+                       goto error;
+               }
+       }
+       memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid));
+
+       dprintk("Server FSID: %llx:%llx\n",
+               (unsigned long long) server->fsid.major,
+               (unsigned long long) server->fsid.minor);
+
+       BUG_ON(!server->nfs_client);
+       BUG_ON(!server->nfs_client->rpc_ops);
+       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
+
+       spin_lock(&nfs_client_lock);
+       list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
+       list_add_tail(&server->master_link, &nfs_volume_list);
+       spin_unlock(&nfs_client_lock);
+
+       server->mount_time = jiffies;
+       return server;
+
+error:
+       nfs_free_server(server);
+       return ERR_PTR(error);
+}
+
+#ifdef CONFIG_NFS_V4
+/*
+ * Initialise an NFS4 client record
+ */
+static int nfs4_init_client(struct nfs_client *clp,
+               int proto, int timeo, int retrans,
+               rpc_authflavor_t authflavour)
+{
+       int error;
+
+       if (clp->cl_cons_state == NFS_CS_READY) {
+               /* the client is initialised already */
+               dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp);
+               return 0;
+       }
+
+       /* Check NFS protocol revision and initialize RPC op vector */
+       clp->rpc_ops = &nfs_v4_clientops;
+
+       error = nfs_create_rpc_client(clp, proto, timeo, retrans, authflavour);
+       if (error < 0)
+               goto error;
+
+       error = nfs_idmap_new(clp);
+       if (error < 0) {
+               dprintk("%s: failed to create idmapper. Error = %d\n",
+                       __FUNCTION__, error);
+               goto error;
+       }
+       __set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
+
+       nfs_mark_client_ready(clp, NFS_CS_READY);
+       return 0;
+
+error:
+       nfs_mark_client_ready(clp, error);
+       dprintk("<-- nfs4_init_client() = xerror %d\n", error);
+       return error;
+}
+
+/*
+ * Set up an NFS4 client
+ */
+static int nfs4_set_client(struct nfs_server *server,
+               const char *hostname, const struct sockaddr_in *addr,
+               rpc_authflavor_t authflavour,
+               int proto, int timeo, int retrans)
+{
+       struct nfs_client *clp;
+       int error;
+
+       dprintk("--> nfs4_set_client()\n");
+
+       /* Allocate or find a client reference we can use */
+       clp = nfs_get_client(hostname, addr, 4);
+       if (IS_ERR(clp)) {
+               error = PTR_ERR(clp);
+               goto error;
+       }
+       error = nfs4_init_client(clp, proto, timeo, retrans, authflavour);
+       if (error < 0)
+               goto error_put;
+
+       server->nfs_client = clp;
+       dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp);
+       return 0;
+
+error_put:
+       nfs_put_client(clp);
+error:
+       dprintk("<-- nfs4_set_client() = xerror %d\n", error);
+       return error;
+}
+
+/*
+ * Create a version 4 volume record
+ */
+static int nfs4_init_server(struct nfs_server *server,
+               const struct nfs4_mount_data *data, rpc_authflavor_t authflavour)
+{
+       int error;
+
+       dprintk("--> nfs4_init_server()\n");
+
+       /* Initialise the client representation from the mount data */
+       server->flags = data->flags & NFS_MOUNT_FLAGMASK;
+       server->caps |= NFS_CAP_ATOMIC_OPEN;
+
+       if (data->rsize)
+               server->rsize = nfs_block_size(data->rsize, NULL);
+       if (data->wsize)
+               server->wsize = nfs_block_size(data->wsize, NULL);
+
+       server->acregmin = data->acregmin * HZ;
+       server->acregmax = data->acregmax * HZ;
+       server->acdirmin = data->acdirmin * HZ;
+       server->acdirmax = data->acdirmax * HZ;
+
+       error = nfs_init_server_rpcclient(server, authflavour);
+
+       /* Done */
+       dprintk("<-- nfs4_init_server() = %d\n", error);
+       return error;
+}
+
+/*
+ * Create a version 4 volume record
+ * - keyed on server and FSID
+ */
+struct nfs_server *nfs4_create_server(const struct nfs4_mount_data *data,
+                                     const char *hostname,
+                                     const struct sockaddr_in *addr,
+                                     const char *mntpath,
+                                     const char *ip_addr,
+                                     rpc_authflavor_t authflavour,
+                                     struct nfs_fh *mntfh)
+{
+       struct nfs_fattr fattr;
+       struct nfs_server *server;
+       int error;
+
+       dprintk("--> nfs4_create_server()\n");
+
+       server = nfs_alloc_server();
+       if (!server)
+               return ERR_PTR(-ENOMEM);
+
+       /* Get a client record */
+       error = nfs4_set_client(server, hostname, addr, authflavour,
+                       data->proto, data->timeo, data->retrans);
+       if (error < 0)
+               goto error;
+
+       /* set up the general RPC client */
+       error = nfs4_init_server(server, data, authflavour);
+       if (error < 0)
+               goto error;
+
+       BUG_ON(!server->nfs_client);
+       BUG_ON(!server->nfs_client->rpc_ops);
+       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
+
+       /* Probe the root fh to retrieve its FSID */
+       error = nfs4_path_walk(server, mntfh, mntpath);
+       if (error < 0)
+               goto error;
+
+       dprintk("Server FSID: %llx:%llx\n",
+               (unsigned long long) server->fsid.major,
+               (unsigned long long) server->fsid.minor);
+       dprintk("Mount FH: %d\n", mntfh->size);
+
+       error = nfs_probe_fsinfo(server, mntfh, &fattr);
+       if (error < 0)
+               goto error;
+
+       BUG_ON(!server->nfs_client);
+       BUG_ON(!server->nfs_client->rpc_ops);
+       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
+
+       spin_lock(&nfs_client_lock);
+       list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
+       list_add_tail(&server->master_link, &nfs_volume_list);
+       spin_unlock(&nfs_client_lock);
+
+       server->mount_time = jiffies;
+       dprintk("<-- nfs4_create_server() = %p\n", server);
+       return server;
+
+error:
+       nfs_free_server(server);
+       dprintk("<-- nfs4_create_server() = error %d\n", error);
+       return ERR_PTR(error);
+}
+
+/*
+ * Create an NFS4 referral server record
+ */
+struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
+                                              struct nfs_fh *fh)
+{
+       struct nfs_client *parent_client;
+       struct nfs_server *server, *parent_server;
+       struct nfs_fattr fattr;
+       int error;
+
+       dprintk("--> nfs4_create_referral_server()\n");
+
+       server = nfs_alloc_server();
+       if (!server)
+               return ERR_PTR(-ENOMEM);
+
+       parent_server = NFS_SB(data->sb);
+       parent_client = parent_server->nfs_client;
+
+       /* Get a client representation.
+        * Note: NFSv4 always uses TCP, */
+       error = nfs4_set_client(server, data->hostname, data->addr,
+                       data->authflavor,
+                       parent_server->client->cl_xprt->prot,
+                       parent_client->retrans_timeo,
+                       parent_client->retrans_count);
+       if (error < 0)
+               goto error;
+
+       /* Initialise the client representation from the parent server */
+       nfs_server_copy_userdata(server, parent_server);
+       server->caps |= NFS_CAP_ATOMIC_OPEN;
+
+       error = nfs_init_server_rpcclient(server, data->authflavor);
+       if (error < 0)
+               goto error;
+
+       BUG_ON(!server->nfs_client);
+       BUG_ON(!server->nfs_client->rpc_ops);
+       BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
+
+       /* probe the filesystem info for this server filesystem */
+       error = nfs_probe_fsinfo(server, fh, &fattr);
+       if (error < 0)
+               goto error;
+
+       dprintk("Referral FSID: %llx:%llx\n",
+               (unsigned long long) server->fsid.major,
+               (unsigned long long) server->fsid.minor);
+
+       spin_lock(&nfs_client_lock);
+       list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
+       list_add_tail(&server->master_link, &nfs_volume_list);
+       spin_unlock(&nfs_client_lock);
+
+       server->mount_time = jiffies;
+
+       dprintk("<-- nfs_create_referral_server() = %p\n", server);
+       return server;
+
+error:
+       nfs_free_server(server);
+       dprintk("<-- nfs4_create_referral_server() = error %d\n", error);
+       return ERR_PTR(error);
+}
+
+#endif /* CONFIG_NFS_V4 */
+
+/*
+ * Clone an NFS2, NFS3 or NFS4 server record
+ */
+struct nfs_server *nfs_clone_server(struct nfs_server *source,
+                                   struct nfs_fh *fh,
+                                   struct nfs_fattr *fattr)
+{
+       struct nfs_server *server;
+       struct nfs_fattr fattr_fsinfo;
+       int error;
+
+       dprintk("--> nfs_clone_server(,%llx:%llx,)\n",
+               (unsigned long long) fattr->fsid.major,
+               (unsigned long long) fattr->fsid.minor);
+
+       server = nfs_alloc_server();
+       if (!server)
+               return ERR_PTR(-ENOMEM);
+
+       /* Copy data from the source */
+       server->nfs_client = source->nfs_client;
+       atomic_inc(&server->nfs_client->cl_count);
+       nfs_server_copy_userdata(server, source);
+
+       server->fsid = fattr->fsid;
+
+       error = nfs_init_server_rpcclient(server, source->client->cl_auth->au_flavor);
+       if (error < 0)
+               goto out_free_server;
+       if (!IS_ERR(source->client_acl))
+               nfs_init_server_aclclient(server);
+
+       /* probe the filesystem info for this server filesystem */
+       error = nfs_probe_fsinfo(server, fh, &fattr_fsinfo);
+       if (error < 0)
+               goto out_free_server;
+
+       dprintk("Cloned FSID: %llx:%llx\n",
+               (unsigned long long) server->fsid.major,
+               (unsigned long long) server->fsid.minor);
+
+       error = nfs_start_lockd(server);
+       if (error < 0)
+               goto out_free_server;
+
+       spin_lock(&nfs_client_lock);
+       list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
+       list_add_tail(&server->master_link, &nfs_volume_list);
+       spin_unlock(&nfs_client_lock);
+
+       server->mount_time = jiffies;
+
+       dprintk("<-- nfs_clone_server() = %p\n", server);
+       return server;
+
+out_free_server:
+       nfs_free_server(server);
+       dprintk("<-- nfs_clone_server() = error %d\n", error);
+       return ERR_PTR(error);
+}
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *proc_fs_nfs;
+
+static int nfs_server_list_open(struct inode *inode, struct file *file);
+static void *nfs_server_list_start(struct seq_file *p, loff_t *pos);
+static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos);
+static void nfs_server_list_stop(struct seq_file *p, void *v);
+static int nfs_server_list_show(struct seq_file *m, void *v);
+
+static struct seq_operations nfs_server_list_ops = {
+       .start  = nfs_server_list_start,
+       .next   = nfs_server_list_next,
+       .stop   = nfs_server_list_stop,
+       .show   = nfs_server_list_show,
+};
+
+static struct file_operations nfs_server_list_fops = {
+       .open           = nfs_server_list_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
+static int nfs_volume_list_open(struct inode *inode, struct file *file);
+static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos);
+static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos);
+static void nfs_volume_list_stop(struct seq_file *p, void *v);
+static int nfs_volume_list_show(struct seq_file *m, void *v);
+
+static struct seq_operations nfs_volume_list_ops = {
+       .start  = nfs_volume_list_start,
+       .next   = nfs_volume_list_next,
+       .stop   = nfs_volume_list_stop,
+       .show   = nfs_volume_list_show,
+};
+
+static struct file_operations nfs_volume_list_fops = {
+       .open           = nfs_volume_list_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
+/*
+ * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which
+ * we're dealing
+ */
+static int nfs_server_list_open(struct inode *inode, struct file *file)
+{
+       struct seq_file *m;
+       int ret;
+
+       ret = seq_open(file, &nfs_server_list_ops);
+       if (ret < 0)
+               return ret;
+
+       m = file->private_data;
+       m->private = PDE(inode)->data;
+
+       return 0;
+}
+
+/*
+ * set up the iterator to start reading from the server list and return the first item
+ */
+static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
+{
+       struct list_head *_p;
+       loff_t pos = *_pos;
+
+       /* lock the list against modification */
+       spin_lock(&nfs_client_lock);
+
+       /* allow for the header line */
+       if (!pos)
+               return SEQ_START_TOKEN;
+       pos--;
+
+       /* find the n'th element in the list */
+       list_for_each(_p, &nfs_client_list)
+               if (!pos--)
+                       break;
+
+       return _p != &nfs_client_list ? _p : NULL;
+}
+
+/*
+ * move to next server
+ */
+static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
+{
+       struct list_head *_p;
+
+       (*pos)++;
+
+       _p = v;
+       _p = (v == SEQ_START_TOKEN) ? nfs_client_list.next : _p->next;
+
+       return _p != &nfs_client_list ? _p : NULL;
+}
+
+/*
+ * clean up after reading from the transports list
+ */
+static void nfs_server_list_stop(struct seq_file *p, void *v)
+{
+       spin_unlock(&nfs_client_lock);
+}
+
+/*
+ * display a header line followed by a load of call lines
+ */
+static int nfs_server_list_show(struct seq_file *m, void *v)
+{
+       struct nfs_client *clp;
+
+       /* display header on line 1 */
+       if (v == SEQ_START_TOKEN) {
+               seq_puts(m, "NV SERVER   PORT USE HOSTNAME\n");
+               return 0;
+       }
+
+       /* display one transport per line on subsequent lines */
+       clp = list_entry(v, struct nfs_client, cl_share_link);
+
+       seq_printf(m, "v%d %02x%02x%02x%02x %4hx %3d %s\n",
+                  clp->cl_nfsversion,
+                  NIPQUAD(clp->cl_addr.sin_addr),
+                  ntohs(clp->cl_addr.sin_port),
+                  atomic_read(&clp->cl_count),
+                  clp->cl_hostname);
+
+       return 0;
+}
+
+/*
+ * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes
+ */
+static int nfs_volume_list_open(struct inode *inode, struct file *file)
+{
+       struct seq_file *m;
+       int ret;
+
+       ret = seq_open(file, &nfs_volume_list_ops);
+       if (ret < 0)
+               return ret;
+
+       m = file->private_data;
+       m->private = PDE(inode)->data;
+
+       return 0;
+}
+
+/*
+ * set up the iterator to start reading from the volume list and return the first item
+ */
+static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
+{
+       struct list_head *_p;
+       loff_t pos = *_pos;
+
+       /* lock the list against modification */
+       spin_lock(&nfs_client_lock);
+
+       /* allow for the header line */
+       if (!pos)
+               return SEQ_START_TOKEN;
+       pos--;
+
+       /* find the n'th element in the list */
+       list_for_each(_p, &nfs_volume_list)
+               if (!pos--)
+                       break;
+
+       return _p != &nfs_volume_list ? _p : NULL;
+}
+
+/*
+ * move to next volume
+ */
+static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
+{
+       struct list_head *_p;
+
+       (*pos)++;
+
+       _p = v;
+       _p = (v == SEQ_START_TOKEN) ? nfs_volume_list.next : _p->next;
+
+       return _p != &nfs_volume_list ? _p : NULL;
+}
+
+/*
+ * clean up after reading from the transports list
+ */
+static void nfs_volume_list_stop(struct seq_file *p, void *v)
+{
+       spin_unlock(&nfs_client_lock);
+}
+
+/*
+ * display a header line followed by a load of call lines
+ */
+static int nfs_volume_list_show(struct seq_file *m, void *v)
+{
+       struct nfs_server *server;
+       struct nfs_client *clp;
+       char dev[8], fsid[17];
+
+       /* display header on line 1 */
+       if (v == SEQ_START_TOKEN) {
+               seq_puts(m, "NV SERVER   PORT DEV     FSID\n");
+               return 0;
+       }
+       /* display one transport per line on subsequent lines */
+       server = list_entry(v, struct nfs_server, master_link);
+       clp = server->nfs_client;
+
+       snprintf(dev, 8, "%u:%u",
+                MAJOR(server->s_dev), MINOR(server->s_dev));
+
+       snprintf(fsid, 17, "%llx:%llx",
+                (unsigned long long) server->fsid.major,
+                (unsigned long long) server->fsid.minor);
+
+       seq_printf(m, "v%d %02x%02x%02x%02x %4hx %-7s %-17s\n",
+                  clp->cl_nfsversion,
+                  NIPQUAD(clp->cl_addr.sin_addr),
+                  ntohs(clp->cl_addr.sin_port),
+                  dev,
+                  fsid);
+
+       return 0;
+}
+
+/*
+ * initialise the /proc/fs/nfsfs/ directory
+ */
+int __init nfs_fs_proc_init(void)
+{
+       struct proc_dir_entry *p;
+
+       proc_fs_nfs = proc_mkdir("nfsfs", proc_root_fs);
+       if (!proc_fs_nfs)
+               goto error_0;
+
+       proc_fs_nfs->owner = THIS_MODULE;
+
+       /* a file of servers with which we're dealing */
+       p = create_proc_entry("servers", S_IFREG|S_IRUGO, proc_fs_nfs);
+       if (!p)
+               goto error_1;
+
+       p->proc_fops = &nfs_server_list_fops;
+       p->owner = THIS_MODULE;
+
+       /* a file of volumes that we have mounted */
+       p = create_proc_entry("volumes", S_IFREG|S_IRUGO, proc_fs_nfs);
+       if (!p)
+               goto error_2;
+
+       p->proc_fops = &nfs_volume_list_fops;
+       p->owner = THIS_MODULE;
+       return 0;
+
+error_2:
+       remove_proc_entry("servers", proc_fs_nfs);
+error_1:
+       remove_proc_entry("nfsfs", proc_root_fs);
+error_0:
+       return -ENOMEM;
+}
+
+/*
+ * clean up the /proc/fs/nfsfs/ directory
+ */
+void nfs_fs_proc_exit(void)
+{
+       remove_proc_entry("volumes", proc_fs_nfs);
+       remove_proc_entry("servers", proc_fs_nfs);
+       remove_proc_entry("nfsfs", proc_root_fs);
+}
+
+#endif /* CONFIG_PROC_FS */
index 9540a31..5713367 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "nfs4_fs.h"
 #include "delegation.h"
+#include "internal.h"
 
 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:
                                /* kill_proc(fl->fl_pid, SIGLOST, 1); */
                        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;
                }
        }
@@ -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)
 {
-       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_delegation *delegation;
        int status = 0;
@@ -145,7 +146,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
                                        sizeof(delegation->stateid)) != 0 ||
                                delegation->type != nfsi->delegation->type) {
                        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;
                }
        }
@@ -176,7 +177,7 @@ static void nfs_msync_inode(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_delegation *delegation;
        int res = 0;
@@ -208,7 +209,7 @@ int __nfs_inode_return_delegation(struct inode *inode)
  */
 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 inode *inode;
 
@@ -232,7 +233,7 @@ restart:
 
 int nfs_do_expire_all_delegations(void *ptr)
 {
-       struct nfs4_client *clp = ptr;
+       struct nfs_client *clp = ptr;
        struct nfs_delegation *delegation;
        struct inode *inode;
 
@@ -254,11 +255,11 @@ restart:
        }
 out:
        spin_unlock(&clp->cl_lock);
-       nfs4_put_client(clp);
+       nfs_put_client(clp);
        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;
 
@@ -266,17 +267,17 @@ void nfs_expire_all_delegations(struct nfs4_client *clp)
        atomic_inc(&clp->cl_count);
        task = kthread_run(nfs_do_expire_all_delegations, clp,
                        "%u.%u.%u.%u-delegreturn",
-                       NIPQUAD(clp->cl_addr));
+                       NIPQUAD(clp->cl_addr.sin_addr));
        if (!IS_ERR(task))
                return;
-       nfs4_put_client(clp);
+       nfs_put_client(clp);
        module_put(THIS_MODULE);
 }
 
 /*
  * 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 inode *inode;
@@ -299,7 +300,7 @@ restart:
 
 struct recall_threadargs {
        struct inode *inode;
-       struct nfs4_client *clp;
+       struct nfs_client *clp;
        const nfs4_stateid *stateid;
 
        struct completion started;
@@ -310,7 +311,7 @@ static int recall_thread(void *data)
 {
        struct recall_threadargs *args = (struct recall_threadargs *)data;
        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_delegation *delegation;
 
@@ -371,7 +372,7 @@ out_module_put:
 /*
  * 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 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
  */
-void nfs_delegation_mark_reclaim(struct nfs4_client *clp)
+void nfs_delegation_mark_reclaim(struct nfs_client *clp)
 {
        struct nfs_delegation *delegation;
        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
  */
-void nfs_delegation_reap_unclaimed(struct nfs4_client *clp)
+void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
 {
        struct nfs_delegation *delegation, *n;
        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)
 {
-       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_delegation *delegation;
        int res = 0;
index 3858694..2cfd4b2 100644 (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_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_expire_all_delegations(struct nfs4_client *clp);
-void nfs_handle_cb_pathdown(struct nfs4_client *clp);
+void nfs_expire_all_delegations(struct nfs_client *clp);
+void nfs_handle_cb_pathdown(struct nfs_client *clp);
 
-void nfs_delegation_mark_reclaim(struct nfs4_client *clp);
-void nfs_delegation_reap_unclaimed(struct nfs4_client *clp);
+void nfs_delegation_mark_reclaim(struct nfs_client *clp);
+void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
 
 /* NFSv4 delegation-related procedures */
 int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid);
index e7ffb4d..3419c2d 100644 (file)
@@ -30,7 +30,9 @@
 #include <linux/nfs_mount.h>
 #include <linux/pagemap.h>
 #include <linux/smp_lock.h>
+#include <linux/pagevec.h>
 #include <linux/namei.h>
+#include <linux/mount.h>
 
 #include "nfs4_fs.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;
 }
 
-static inline int nfs_reval_fsid(struct inode *dir,
-               struct nfs_fh *fh, struct nfs_fattr *fattr)
+static inline int nfs_reval_fsid(struct vfsmount *mnt, struct inode *dir,
+                                struct nfs_fh *fh, struct nfs_fattr *fattr)
 {
        struct nfs_server *server = NFS_SERVER(dir);
 
        if (!nfs_fsid_equal(&server->fsid, &fattr->fsid))
                /* 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;
 }
 
@@ -902,9 +904,15 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
 
        lock_kernel();
 
-       /* If we're doing an exclusive create, optimize away the lookup */
-       if (nfs_is_exclusive_create(dir, nd))
-               goto no_entry;
+       /*
+        * If we're doing an exclusive create, optimize away the lookup
+        * 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);
        if (error == -ENOENT)
@@ -913,7 +921,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
                res = ERR_PTR(error);
                goto out_unlock;
        }
-       error = nfs_reval_fsid(dir, &fhandle, &fattr);
+       error = nfs_reval_fsid(nd->mnt, dir, &fhandle, &fattr);
        if (error < 0) {
                res = ERR_PTR(error);
                goto out_unlock;
@@ -922,8 +930,9 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
        res = (struct dentry *)inode;
        if (IS_ERR(res))
                goto out_unlock;
+
 no_entry:
-       res = d_add_unique(dentry, inode);
+       res = d_materialise_unique(dentry, inode);
        if (res != NULL)
                dentry = res;
        nfs_renew_times(dentry);
@@ -1117,11 +1126,13 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc)
                dput(dentry);
                return NULL;
        }
-       alias = d_add_unique(dentry, inode);
+
+       alias = d_materialise_unique(dentry, inode);
        if (alias != NULL) {
                dput(dentry);
                dentry = alias;
        }
+
        nfs_renew_times(dentry);
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        return dentry;
@@ -1143,23 +1154,22 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
                struct inode *dir = dentry->d_parent->d_inode;
                error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
                if (error)
-                       goto out_err;
+                       return error;
        }
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
                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)
-                       goto out_err;
+                       return error;
        }
        inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
        error = PTR_ERR(inode);
        if (IS_ERR(inode))
-               goto out_err;
+               return error;
        d_instantiate(dentry, inode);
+       if (d_unhashed(dentry))
+               d_rehash(dentry);
        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;
 }
 
-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 nfs_fattr sym_attr;
-       struct nfs_fh sym_fh;
-       struct qstr qsymname;
+       unsigned int pathlen = strlen(symname);
        int error;
 
        dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
                dir->i_ino, dentry->d_name.name, symname);
 
-#ifdef NFS_PARANOIA
-if (dentry->d_inode)
-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;
+       if (pathlen > PAGE_SIZE)
+               return -ENAMETOOLONG;
 
-       qsymname.name = symname;
-       qsymname.len  = strlen(symname);
+       attr.ia_mode = S_IFLNK | S_IRWXUGO;
+       attr.ia_valid = ATTR_MODE;
 
        lock_kernel();
+
+       page = alloc_page(GFP_KERNEL);
+       if (!page) {
+               unlock_kernel();
+               return -ENOMEM;
+       }
+
+       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->d_name, &qsymname,
-                                         &attr, &sym_fh, &sym_attr);
+       error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
        nfs_end_data_update(dir);
-       if (!error) {
-               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);
+       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();
-       return error;
+       return 0;
 }
 
 static int 
@@ -1638,35 +1682,211 @@ out:
        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)
 {
        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
-                       || time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
-                       || (nfsi->cache_validity & NFS_INO_INVALID_ACCESS))
-               return -ENOENT;
-       memcpy(res, cache, sizeof(*res));
-       return 0;
+       spin_lock(&inode->i_lock);
+       if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)
+               goto out_zap;
+       cache = nfs_access_search_rbtree(inode, cred);
+       if (cache == NULL)
+               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;
 }
 
-void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
+static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
-       struct nfs_access_entry *cache = &nfsi->cache_access;
+       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;
 
-       if (cache->cred != set->cred) {
-               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;
+       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)
+{
+       struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL);
+       if (cache == NULL)
+               return;
+       RB_CLEAR_NODE(&cache->rb_node);
        cache->jiffies = set->jiffies;
+       cache->cred = get_rpccred(set->cred);
        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)
index 48e8928..be997d6 100644 (file)
@@ -111,7 +111,7 @@ nfs_file_open(struct inode *inode, struct file *filp)
 
        nfs_inc_stats(inode, NFSIOS_VFSOPEN);
        lock_kernel();
-       res = NFS_SERVER(inode)->rpc_ops->file_open(inode, filp);
+       res = NFS_PROTO(inode)->file_open(inode, filp);
        unlock_kernel();
        return res;
 }
@@ -157,7 +157,7 @@ force_reval:
 static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
 {
        /* origin == SEEK_END => we must revalidate the cached file length */
-       if (origin == 2) {
+       if (origin == SEEK_END) {
                struct inode *inode = filp->f_mapping->host;
                int retval = nfs_revalidate_file_size(inode, filp);
                if (retval < 0)
diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c
new file mode 100644 (file)
index 0000000..76b08ae
--- /dev/null
@@ -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 */
index 07a5dd5..82ad711 100644 (file)
 /* Default cache timeout is 10 minutes */
 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 {
        unsigned long ih_expires;
        __u32 ih_id;
@@ -70,7 +84,6 @@ struct idmap_hashtable {
 };
 
 struct idmap {
-       char                  idmap_path[48];
        struct dentry        *idmap_dentry;
        wait_queue_head_t     idmap_wq;
        struct idmap_msg      idmap_im;
@@ -94,24 +107,23 @@ static struct rpc_pipe_ops idmap_upcall_ops = {
         .destroy_msg    = idmap_pipe_destroy_msg,
 };
 
-void
-nfs_idmap_new(struct nfs4_client *clp)
+int
+nfs_idmap_new(struct nfs_client *clp)
 {
        struct idmap *idmap;
+       int error;
 
-       if (clp->cl_idmap != NULL)
-               return;
-        if ((idmap = kzalloc(sizeof(*idmap), GFP_KERNEL)) == NULL)
-                return;
+       BUG_ON(clp->cl_idmap != NULL);
 
-       snprintf(idmap->idmap_path, sizeof(idmap->idmap_path),
-           "%s/idmap", clp->cl_rpcclient->cl_pathname);
+        if ((idmap = kzalloc(sizeof(*idmap), GFP_KERNEL)) == NULL)
+                return -ENOMEM;
 
-        idmap->idmap_dentry = rpc_mkpipe(idmap->idmap_path,
+        idmap->idmap_dentry = rpc_mkpipe(clp->cl_rpcclient->cl_dentry, "idmap",
            idmap, &idmap_upcall_ops, 0);
         if (IS_ERR(idmap->idmap_dentry)) {
+               error = PTR_ERR(idmap->idmap_dentry);
                kfree(idmap);
-               return;
+               return error;
        }
 
         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;
 
        clp->cl_idmap = idmap;
+       return 0;
 }
 
 void
-nfs_idmap_delete(struct nfs4_client *clp)
+nfs_idmap_delete(struct nfs_client *clp)
 {
        struct idmap *idmap = clp->cl_idmap;
 
@@ -477,27 +490,27 @@ static unsigned int fnvhash32(const void *buf, size_t buflen)
        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;
 
        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;
 
        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;
 
        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;
 
index d349fb2..e8c143d 100644 (file)
@@ -76,19 +76,14 @@ int nfs_write_inode(struct inode *inode, int sync)
 
 void nfs_clear_inode(struct inode *inode)
 {
-       struct nfs_inode *nfsi = NFS_I(inode);
-       struct rpc_cred *cred;
-
        /*
         * The following should never happen...
         */
        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);
-       cred = nfsi->cache_access.cred;
-       if (cred)
-               put_rpccred(cred);
-       BUG_ON(atomic_read(&nfsi->data_updates) != 0);
+       nfs_access_zap_cache(inode);
 }
 
 /**
@@ -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
                 * 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)) {
                        inode->i_fop = &nfs_file_operations;
                        inode->i_data.a_ops = &nfs_file_aops;
                        inode->i_data.backing_dev_info = &NFS_SB(sb)->backing_dev_info;
                } 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;
                        if (nfs_server_capable(inode, NFS_CAP_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_timestamp = jiffies;
                memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
-               nfsi->cache_access.cred = NULL;
+               nfsi->access_cache = RB_ROOT;
 
                unlock_new_inode(inode);
        } else
@@ -722,13 +717,11 @@ void nfs_end_data_update(struct inode *inode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
 
-       if (!nfs_have_delegation(inode, FMODE_READ)) {
-               /* Directories and symlinks: invalidate page cache */
-               if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
-                       spin_lock(&inode->i_lock);
-                       nfsi->cache_validity |= NFS_INO_INVALID_DATA;
-                       spin_unlock(&inode->i_lock);
-               }
+       /* Directories: invalidate page cache */
+       if (S_ISDIR(inode->i_mode)) {
+               spin_lock(&inode->i_lock);
+               nfsi->cache_validity |= NFS_INO_INVALID_DATA;
+               spin_unlock(&inode->i_lock);
        }
        nfsi->cache_change_attribute = jiffies;
        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
  * 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)
 {
@@ -1025,7 +1024,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
  out_fileid:
        printk(KERN_ERR "NFS: server %s error: fileid changed\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);
        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->commit);
                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);
                atomic_set(&nfsi->data_updates, 0);
                nfsi->ndirty = 0;
@@ -1144,6 +1145,10 @@ static int __init init_nfs_fs(void)
 {
        int err;
 
+       err = nfs_fs_proc_init();
+       if (err)
+               goto out5;
+
        err = nfs_init_nfspagecache();
        if (err)
                goto out4;
@@ -1184,6 +1189,8 @@ out2:
 out3:
        nfs_destroy_nfspagecache();
 out4:
+       nfs_fs_proc_exit();
+out5:
        return err;
 }
 
@@ -1198,6 +1205,7 @@ static void __exit exit_nfs_fs(void)
        rpc_proc_unregister("nfs");
 #endif
        unregister_nfs_fs();
+       nfs_fs_proc_exit();
 }
 
 /* Not quite true; I just maintain it */
index e4f4e5d..bea0b01 100644 (file)
@@ -4,6 +4,18 @@
 
 #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 {
        const struct super_block *sb;
        const struct dentry *dentry;
@@ -15,7 +27,40 @@ struct nfs_clone_mount {
        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
 extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry);
 #else
@@ -46,6 +91,7 @@ extern void nfs_destroy_directcache(void);
 #endif
 
 /* nfs2xdr.c */
+extern int nfs_stat_to_errno(int);
 extern struct rpc_procinfo nfs_procedures[];
 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);
 
 /* 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);
+#endif
 
 /* nfs4proc.c */
 #ifdef CONFIG_NFS_V4
@@ -66,6 +113,9 @@ extern int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry,
                                  struct page *page);
 #endif
 
+/* dir.c */
+extern int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask);
+
 /* inode.c */
 extern struct inode *nfs_alloc_inode(struct super_block *sb);
 extern void nfs_destroy_inode(struct inode *);
@@ -76,10 +126,10 @@ extern void nfs4_clear_inode(struct inode *);
 #endif
 
 /* super.c */
-extern struct file_system_type nfs_referral_nfs4_fs_type;
-extern struct file_system_type clone_nfs_fs_type;
+extern struct file_system_type nfs_xdev_fs_type;
 #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
 
 extern struct rpc_stat nfs_rpcstat;
@@ -88,30 +138,30 @@ extern int __init register_nfs_fs(void);
 extern void __exit unregister_nfs_fs(void);
 
 /* 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);
 
-/*
- * Determine the mount path as a string
- */
-static inline char *
-nfs4_path(const struct dentry *dentry, char *buffer, ssize_t buflen)
-{
+/* getroot.c */
+extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *);
 #ifdef CONFIG_NFS_V4
-       return nfs_path(NFS_SB(dentry->d_sb)->mnt_path, dentry, buffer, buflen);
-#else
-       return NULL;
+extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *);
+
+extern int nfs4_path_walk(struct nfs_server *server,
+                         struct nfs_fh *mntfh,
+                         const char *path);
 #endif
-}
 
 /*
  * Determine the device name as a string
  */
 static inline char *nfs_devname(const struct vfsmount *mnt_parent,
-                        const struct dentry *dentry,
-                        char *buffer, ssize_t buflen)
+                               const struct dentry *dentry,
+                               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)
                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;
-}
index 445abb4..d507b02 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/net.h>
 #include <linux/in.h>
 #include <linux/sunrpc/clnt.h>
-#include <linux/sunrpc/xprt.h>
 #include <linux/sunrpc/sched.h>
 #include <linux/nfs_fs.h>
 
@@ -77,22 +76,19 @@ static struct rpc_clnt *
 mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version,
                int protocol)
 {
-       struct rpc_xprt *xprt;
-       struct rpc_clnt *clnt;
-
-       xprt = xprt_create_proto(protocol, srvaddr, NULL);
-       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;
+       struct rpc_create_args args = {
+               .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),
+       };
+
+       return rpc_create(&args);
 }
 
 /*
index 86b3169..77b0068 100644 (file)
@@ -2,6 +2,7 @@
  * linux/fs/nfs/namespace.c
  *
  * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
+ * - Modified by David Howells <dhowells@redhat.com>
  *
  * NFS namespace
  */
@@ -28,6 +29,7 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;
 /*
  * nfs_path - reconstruct the path given an arbitrary dentry
  * @base - arbitrary string to prepend to the path
+ * @droot - pointer to root dentry for mountpoint
  * @dentry - pointer to dentry
  * @buffer - result 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
  * 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 *end = buffer+buflen;
@@ -47,7 +51,7 @@ char *nfs_path(const char *base, const struct dentry *dentry,
        *--end = '\0';
        buflen--;
        spin_lock(&dcache_lock);
-       while (!IS_ROOT(dentry)) {
+       while (!IS_ROOT(dentry) && dentry != droot) {
                namelen = dentry->d_name.len;
                buflen -= namelen + 1;
                if (buflen < 0)
@@ -96,15 +100,18 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
        struct nfs_fattr fattr;
        int err;
 
+       dprintk("--> nfs_follow_mountpoint()\n");
+
        BUG_ON(IS_ROOT(dentry));
        dprintk("%s: enter\n", __FUNCTION__);
        dput(nd->dentry);
        nd->dentry = dget(dentry);
-       if (d_mountpoint(nd->dentry))
-               goto out_follow;
+
        /* Look it up again */
        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);
        if (err != 0)
                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);
 out:
        dprintk("%s: done, returned %d\n", __FUNCTION__, err);
+
+       dprintk("<-- nfs_follow_mountpoint() = %d\n", err);
        return ERR_PTR(err);
 out_err:
        path_release(nd);
@@ -172,22 +181,23 @@ void nfs_release_automount_timer(void)
 /*
  * 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)
 {
 #ifdef CONFIG_NFS_V4
        struct vfsmount *mnt = NULL;
-       switch (server->rpc_ops->version) {
+       switch (server->nfs_client->cl_nfsversion) {
                case 2:
                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;
                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;
 #else
-       return vfs_kern_mount(&clone_nfs_fs_type, 0, devname, mountdata);
+       return vfs_kern_mount(&nfs_xdev_fs_type, 0, devname, mountdata);
 #endif
 }
 
@@ -213,6 +223,8 @@ struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent,
        char *page = (char *) __get_free_page(GFP_USER);
        char *devname;
 
+       dprintk("--> nfs_do_submount()\n");
+
        dprintk("%s: submounting on %s/%s\n", __FUNCTION__,
                        dentry->d_parent->d_name.name,
                        dentry->d_name.name);
@@ -227,5 +239,7 @@ free_page:
        free_page((unsigned long)page);
 out:
        dprintk("%s: done\n", __FUNCTION__);
+
+       dprintk("<-- nfs_do_submount() = %p\n", mnt);
        return mnt;
 }
index 67391ee..b49501f 100644 (file)
@@ -51,7 +51,7 @@
 #define NFS_createargs_sz      (NFS_diropargs_sz+NFS_sattr_sz)
 #define NFS_renameargs_sz      (NFS_diropargs_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_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
 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_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);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       sndbuf->len += xdr_adjust_iovec(sndbuf->tail, p) - pad;
        return 0;
 }
 
index 7143b1f..f8688ea 100644 (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
 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;
 
        status = do_proc_get_root(server->client, fhandle, info);
-       if (status && server->client_sys != server->client)
-               status = do_proc_get_root(server->client_sys, fhandle, info);
+       if (status && server->nfs_client->cl_rpcclient != server->client)
+               status = do_proc_get_root(server->nfs_client->cl_rpcclient, fhandle, info);
        return status;
 }
 
@@ -544,23 +544,23 @@ nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
 }
 
 static int
-nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
-                 struct iattr *sattr, struct nfs_fh *fhandle,
-                 struct nfs_fattr *fattr)
+nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
+                 unsigned int len, struct iattr *sattr)
 {
-       struct nfs_fattr        dir_attr;
+       struct nfs_fh fhandle;
+       struct nfs_fattr fattr, dir_attr;
        struct nfs3_symlinkargs arg = {
                .fromfh         = NFS_FH(dir),
-               .fromname       = name->name,
-               .fromlen        = name->len,
-               .topath         = path->name,
-               .tolen          = path->len,
+               .fromname       = dentry->d_name.name,
+               .fromlen        = dentry->d_name.len,
+               .pages          = &page,
+               .pathlen        = len,
                .sattr          = sattr
        };
        struct nfs3_diropres    res = {
                .dir_attr       = &dir_attr,
-               .fh             = fhandle,
-               .fattr          = fattr
+               .fh             = &fhandle,
+               .fattr          = &fattr
        };
        struct rpc_message msg = {
                .rpc_proc       = &nfs3_procedures[NFS3PROC_SYMLINK],
@@ -569,13 +569,19 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
        };
        int                     status;
 
-       if (path->len > NFS3_MAXPATHLEN)
+       if (len > NFS3_MAXPATHLEN)
                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(fattr);
+       nfs_fattr_init(&fattr);
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
        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);
        return status;
 }
@@ -785,7 +791,7 @@ nfs3_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
 
        dprintk("NFS call  fsinfo\n");
        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);
        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);
 }
 
-struct nfs_rpc_ops     nfs_v3_clientops = {
+const struct nfs_rpc_ops nfs_v3_clientops = {
        .version        = 3,                    /* protocol version */
        .dentry_ops     = &nfs_dentry_operations,
        .dir_inode_ops  = &nfs3_dir_inode_operations,
index 0250269..16556fa 100644 (file)
@@ -56,7 +56,7 @@
 #define NFS3_writeargs_sz      (NFS3_fh_sz+5)
 #define NFS3_createargs_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_renameargs_sz     (NFS3_diropargs_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_array(p, args->fromname, args->fromlen);
        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);
+
+       /* Copy the page */
+       xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
        return 0;
 }
 
index 9a10286..61095fe 100644 (file)
@@ -42,55 +42,6 @@ enum nfs4_client_state {
        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
  * 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 {
        spinlock_t           so_lock;
        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 */
        atomic_t             so_count;
 
@@ -210,10 +161,10 @@ extern ssize_t nfs4_listxattr(struct dentry *, char *, size_t);
 
 /* nfs4proc.c */
 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_confirm(struct nfs4_client *, struct rpc_cred *);
-extern int nfs4_proc_async_renew(struct nfs4_client *, struct rpc_cred *);
-extern int nfs4_proc_renew(struct nfs4_client *, struct rpc_cred *);
+extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *);
+extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *);
+extern int nfs4_proc_async_renew(struct nfs_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 struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, 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];
 
 /* 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_kill_renewd(struct nfs4_client *);
+extern void nfs4_kill_renewd(struct nfs_client *);
 extern void nfs4_renew_state(void *);
 
 /* nfs4state.c */
-extern void init_nfsv4_state(struct nfs_server *);
-extern void destroy_nfsv4_state(struct nfs_server *);
-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 *);
+struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp);
+extern u32 nfs4_alloc_lockowner_id(struct nfs_client *);
 
 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 *);
@@ -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_close_state(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 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);
@@ -276,10 +222,6 @@ extern struct svc_version nfs4_callback_version1;
 
 #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)
 
 #endif /* CONFIG_NFS_V4 */
index ea38d27..24e47f3 100644 (file)
@@ -2,6 +2,7 @@
  * linux/fs/nfs/nfs4namespace.c
  *
  * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
+ * - Modified by David Howells <dhowells@redhat.com>
  *
  * NFSv4 namespace
  */
@@ -23,7 +24,7 @@
 /*
  * 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 *end = buffer + buflen;
@@ -34,7 +35,7 @@ static inline char *nfs4_pathname_string(struct nfs4_pathname *pathname,
 
        n = pathname->ncomponents;
        while (--n >= 0) {
-               struct nfs4_string *component = &pathname->components[n];
+               const struct nfs4_string *component = &pathname->components[n];
                buflen -= component->len + 1;
                if (buflen < 0)
                        goto Elong;
@@ -47,6 +48,68 @@ Elong:
        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
@@ -60,7 +123,7 @@ Elong:
  */
 static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
                                            const struct dentry *dentry,
-                                           struct nfs4_fs_locations *locations)
+                                           const struct nfs4_fs_locations *locations)
 {
        struct vfsmount *mnt = ERR_PTR(-ENOENT);
        struct nfs_clone_mount mountdata = {
@@ -68,10 +131,9 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
                .dentry = dentry,
                .authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor,
        };
-       char *page, *page2;
-       char *path, *fs_path;
+       char *page = NULL, *page2 = NULL;
        char *devname;
-       int loc, s;
+       int loc, s, error;
 
        if (locations == NULL || locations->nlocations <= 0)
                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__,
                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);
-       if (page == NULL)
+       if (!page)
                goto out;
+
        page2 = (char *) __get_free_page(GFP_USER);
-       if (page2 == NULL)
+       if (!page2)
                goto out;
 
-       path = nfs4_path(dentry, page, PAGE_SIZE);
-       if (IS_ERR(path))
-               goto out_free;
-
-       fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE);
-       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;
+       /* Ensure fs path is a prefix of current dentry path */
+       error = nfs4_validate_fspath(mnt_parent, dentry, locations, page, page2);
+       if (error < 0) {
+               mnt = ERR_PTR(error);
+               goto out;
        }
 
        devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE);
        if (IS_ERR(devname)) {
                mnt = (struct vfsmount *)devname;
-               goto out_free;
+               goto out;
        }
 
        loc = 0;
        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;
 
                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);
                        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)) {
                                break;
                        }
@@ -149,10 +205,9 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
                loc++;
        }
 
-out_free:
-       free_page((unsigned long)page);
-       free_page((unsigned long)page2);
 out:
+       free_page((unsigned long) page);
+       free_page((unsigned long) page2);
        dprintk("%s: done\n", __FUNCTION__);
        return mnt;
 }
@@ -165,7 +220,7 @@ out:
  */
 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 nfs4_fs_locations *fs_locations = NULL;
        struct page *page;
@@ -183,11 +238,16 @@ struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentr
                goto out_free;
 
        /* Get locations */
+       mnt = ERR_PTR(-ENOENT);
+
        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);
        dput(parent);
-       if (err != 0 || fs_locations->nlocations <= 0 ||
+       if (err != 0 ||
+           fs_locations->nlocations <= 0 ||
            fs_locations->fs_path.ncomponents <= 0)
                goto out_free;
 
index b14145b..47c7e6e 100644 (file)
@@ -55,7 +55,7 @@
 
 #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)
 
 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_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_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 */
 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)
 {
-       struct nfs4_client *clp = server->nfs4_state;
+       struct nfs_client *clp = server->nfs_client;
        spin_lock(&clp->cl_lock);
        if (time_before(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);
        p->o_arg.fh = NFS_FH(dir);
        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.name = &dentry->d_name;
        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_EXPIRED:
                                /* 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;
                }
                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);
        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;
 }
 
@@ -792,11 +792,18 @@ out:
 
 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);
-       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_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 nfs4_state_owner *sp = 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     *state = NULL;
        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;
        int                     status;
 
@@ -1133,7 +1140,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                        break;
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_EXPIRED:
-                       nfs4_schedule_state_recovery(server->nfs4_state);
+                       nfs4_schedule_state_recovery(server->nfs_client);
                        break;
                default:
                        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);
        }
 
-       cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
+       cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
        if (IS_ERR(cred))
                return (struct dentry *)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 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))
                return PTR_ERR(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;
 }
 
+/*
+ * get the file handle for the "/" directory on the server
+ */
 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;
 
-       /*
-        * 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);
-       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)
                status = nfs4_server_capabilities(server, fhandle);
        if (status == 0)
                status = nfs4_do_fsinfo(server, fhandle, info);
-out:
        return nfs4_map_errors(status);
 }
 
@@ -1565,7 +1521,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *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))
                return PTR_ERR(cred);
 
@@ -1583,6 +1539,52 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
        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,
                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;
        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)) {
                status = PTR_ERR(cred);
                goto out;
@@ -2089,24 +2091,24 @@ static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *n
        return err;
 }
 
-static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name,
-               struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle,
-               struct nfs_fattr *fattr)
+static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
+               struct page *page, unsigned int len, struct iattr *sattr)
 {
        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 = {
                .dir_fh = NFS_FH(dir),
                .server = server,
-               .name = name,
+               .name = &dentry->d_name,
                .attrs = sattr,
                .ftype = NF4LNK,
                .bitmask = server->attr_bitmask,
        };
        struct nfs4_create_res res = {
                .server = server,
-               .fh = fhandle,
-               .fattr = fattr,
+               .fh = &fhandle,
+               .fattr = &fattr,
                .dir_fattr = &dir_fattr,
        };
        struct rpc_message msg = {
@@ -2116,29 +2118,32 @@ static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name,
        };
        int                     status;
 
-       if (path->len > NFS4_MAXPATHLEN)
+       if (len > NFS4_MAXPATHLEN)
                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);
        
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
-       if (!status)
+       if (!status) {
                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;
 }
 
-static int nfs4_proc_symlink(struct inode *dir, struct qstr *name,
-               struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle,
-               struct nfs_fattr *fattr)
+static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
+               struct page *page, unsigned int len, struct iattr *sattr)
 {
        struct nfs4_exception exception = { };
        int err;
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
-                               _nfs4_proc_symlink(dir, name, path, sattr,
-                                       fhandle, fattr),
+                               _nfs4_proc_symlink(dir, dentry, page,
+                                                       len, sattr),
                                &exception);
        } while (exception.retry);
        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)
 {
-       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;
 
        if (task->tk_status < 0) {
@@ -2543,7 +2548,7 @@ static const struct rpc_call_ops nfs4_renew_ops = {
        .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 = {
                .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);
 }
 
-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 = {
                .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;
        nfs_inode_return_delegation(inode);
        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)
                nfs4_write_cached_acl(inode, buf, buflen);
        return ret;
@@ -2791,7 +2796,7 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
 static int
 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)
                return 0;
@@ -2828,7 +2833,7 @@ static int nfs4_wait_bit_interruptible(void *word)
        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;
        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)
 {
-       struct nfs4_client *clp = server->nfs4_state;
+       struct nfs_client *clp = server->nfs_client;
        int ret = errorcode;
 
        exception->retry = 0;
@@ -2886,6 +2891,7 @@ int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct
                        if (ret == 0)
                                exception->retry = 1;
                        break;
+               case -NFS4ERR_FILE_OPEN:
                case -NFS4ERR_GRACE:
                case -NFS4ERR_DELAY:
                        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);
 }
 
-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;
        struct nfs4_setclientid setclientid = {
@@ -2922,7 +2928,7 @@ int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short p
        for(;;) {
                setclientid.sc_name_len = scnprintf(setclientid.sc_name,
                                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,
                                clp->cl_id_uniquifier);
                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;
 }
 
-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 rpc_message msg = {
@@ -2969,7 +2975,7 @@ static int _nfs4_proc_setclientid_confirm(struct nfs4_client *clp, struct rpc_cr
        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;
        int err;
@@ -3077,7 +3083,7 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4
                switch (err) {
                        case -NFS4ERR_STALE_STATEID:
                        case -NFS4ERR_EXPIRED:
-                               nfs4_schedule_state_recovery(server->nfs4_state);
+                               nfs4_schedule_state_recovery(server->nfs_client);
                        case 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 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 = {
                .fh = NFS_FH(inode),
                .fl = request,
@@ -3231,7 +3237,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
                        break;
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_EXPIRED:
-                       nfs4_schedule_state_recovery(calldata->server->nfs4_state);
+                       nfs4_schedule_state_recovery(calldata->server->nfs_client);
                        break;
                default:
                        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)
                goto out_free;
        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->lsp = lsp;
        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)
 {
-       struct nfs4_client *clp = state->owner->so_client;
+       struct nfs_client *clp = state->owner->so_client;
        unsigned char fl_flags = request->fl_flags;
        int status;
 
@@ -3715,7 +3721,7 @@ static struct inode_operations nfs4_file_inode_operations = {
        .listxattr      = nfs4_listxattr,
 };
 
-struct nfs_rpc_ops     nfs_v4_clientops = {
+const struct nfs_rpc_ops nfs_v4_clientops = {
        .version        = 4,                    /* protocol version */
        .dentry_ops     = &nfs4_dentry_operations,
        .dir_inode_ops  = &nfs4_dir_inode_operations,
@@ -3723,6 +3729,7 @@ struct nfs_rpc_ops        nfs_v4_clientops = {
        .getroot        = nfs4_proc_get_root,
        .getattr        = nfs4_proc_getattr,
        .setattr        = nfs4_proc_setattr,
+       .lookupfh       = nfs4_proc_lookupfh,
        .lookup         = nfs4_proc_lookup,
        .access         = nfs4_proc_access,
        .readlink       = nfs4_proc_readlink,
@@ -3743,6 +3750,7 @@ struct nfs_rpc_ops        nfs_v4_clientops = {
        .statfs         = nfs4_proc_statfs,
        .fsinfo         = nfs4_proc_fsinfo,
        .pathconf       = nfs4_proc_pathconf,
+       .set_capabilities = nfs4_server_capabilities,
        .decode_dirent  = nfs4_decode_dirent,
        .read_setup     = nfs4_proc_read_setup,
        .read_done      = nfs4_read_done,
index 5d764d8..7b6df18 100644 (file)
@@ -61,7 +61,7 @@
 void
 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;
        long lease, timeout;
        unsigned long last, now;
@@ -108,7 +108,7 @@ out:
 
 /* Must be called with clp->cl_sem locked for writes */
 void
-nfs4_schedule_state_renewal(struct nfs4_client *clp)
+nfs4_schedule_state_renewal(struct nfs_client *clp)
 {
        long timeout;
 
@@ -121,32 +121,20 @@ nfs4_schedule_state_renewal(struct nfs4_client *clp)
                        __FUNCTION__, (timeout + HZ - 1) / HZ);
        cancel_delayed_work(&clp->cl_renewd);
        schedule_delayed_work(&clp->cl_renewd, timeout);
+       set_bit(NFS_CS_RENEWD, &clp->cl_res_state);
        spin_unlock(&clp->cl_lock);
 }
 
 void
 nfs4_renewd_prepare_shutdown(struct nfs_server *server)
 {
-       struct nfs4_client *clp = server->nfs4_state;
-
-       if (!clp)
-               return;
        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
-nfs4_kill_renewd(struct nfs4_client *clp)
+nfs4_kill_renewd(struct nfs_client *clp)
 {
        down_read(&clp->cl_sem);
-       if (!list_empty(&clp->cl_superblocks)) {
-               up_read(&clp->cl_sem);
-               return;
-       }
        cancel_delayed_work(&clp->cl_renewd);
        up_read(&clp->cl_sem);
        flush_scheduled_work();
index 090a36b..5fffbdf 100644 (file)
 #include "nfs4_fs.h"
 #include "callback.h"
 #include "delegation.h"
+#include "internal.h"
 
 #define OPENOWNER_POOL_SIZE    8
 
 const nfs4_stateid zero_stateid;
 
-static DEFINE_SPINLOCK(state_spinlock);
 static LIST_HEAD(nfs4_clientid_list);
 
-void
-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)
+static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
 {
        int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK,
                        nfs_callback_tcpport, cred);
@@ -204,13 +70,13 @@ static int nfs4_init_client(struct nfs4_client *clp, struct rpc_cred *cred)
 }
 
 u32
-nfs4_alloc_lockowner_id(struct nfs4_client *clp)
+nfs4_alloc_lockowner_id(struct nfs_client *clp)
 {
        return clp->cl_lockowner_id ++;
 }
 
 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;
 
@@ -224,7 +90,7 @@ nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred)
        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 rpc_cred *cred = NULL;
@@ -238,7 +104,7 @@ struct rpc_cred *nfs4_get_renew_cred(struct nfs4_client *clp)
        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;
 
@@ -251,7 +117,7 @@ struct rpc_cred *nfs4_get_setclientid_cred(struct nfs4_client *clp)
 }
 
 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;
 
@@ -294,7 +160,7 @@ nfs4_alloc_state_owner(void)
 void
 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);
        list_del_init(&sp->so_list);
        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_client *clp = server->nfs4_state;
+       struct nfs_client *clp = server->nfs_client;
        struct nfs4_state_owner *sp, *new;
 
        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)
 {
-       struct nfs4_client *clp = sp->so_client;
+       struct nfs_client *clp = sp->so_client;
        struct rpc_cred *cred = sp->so_cred;
 
        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)
 {
        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);
        if (lsp == NULL)
@@ -752,7 +618,7 @@ out:
 
 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();
        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
  */
-static void nfs4_recover_state(struct nfs4_client *clp)
+static void nfs4_recover_state(struct nfs_client *clp)
 {
        struct task_struct *task;
 
        __module_get(THIS_MODULE);
        atomic_inc(&clp->cl_count);
        task = kthread_run(reclaimer, clp, "%u.%u.%u.%u-reclaim",
-                       NIPQUAD(clp->cl_addr));
+                       NIPQUAD(clp->cl_addr.sin_addr));
        if (!IS_ERR(task))
                return;
        nfs4_clear_recover_bit(clp);
-       nfs4_put_client(clp);
+       nfs_put_client(clp);
        module_put(THIS_MODULE);
 }
 
 /*
  * 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)
                return;
@@ -879,7 +745,7 @@ out_err:
        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 *state;
@@ -903,7 +769,7 @@ static void nfs4_state_mark_reclaim(struct nfs4_client *clp)
 
 static int reclaimer(void *ptr)
 {
-       struct nfs4_client *clp = ptr;
+       struct nfs_client *clp = ptr;
        struct nfs4_state_owner *sp;
        struct nfs4_state_recovery_ops *ops;
        struct rpc_cred *cred;
@@ -970,12 +836,12 @@ out:
        if (status == -NFS4ERR_CB_PATH_DOWN)
                nfs_handle_cb_pathdown(clp);
        nfs4_clear_recover_bit(clp);
-       nfs4_put_client(clp);
+       nfs_put_client(clp);
        module_put_and_exit(0);
        return 0;
 out_error:
        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);
        goto out;
 }
index 730ec8f..3dd413f 100644 (file)
@@ -58,7 +58,7 @@
 /* Mapping from NFS error code to "errno" error code. */
 #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 */
 #ifdef DEBUG
@@ -128,7 +128,7 @@ static int nfs_stat_to_errno(int);
 #define decode_link_maxsz      (op_decode_hdr_maxsz + 5)
 #define encode_symlink_maxsz   (op_encode_hdr_maxsz + \
                                1 + nfs4_name_maxsz + \
-                               nfs4_path_maxsz + \
+                               1 + \
                                nfs4_fattr_maxsz)
 #define decode_symlink_maxsz   (op_decode_hdr_maxsz + 8)
 #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)
                len += 4;
        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) {
                        printk(KERN_WARNING "nfs: couldn't resolve uid %d to string\n",
                               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);
        }
        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) {
                        printk(KERN_WARNING "nfs4: couldn't resolve gid %d to string\n",
                               iap->ia_gid);
@@ -673,9 +673,9 @@ static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *c
 
        switch (create->ftype) {
        case NF4LNK:
-               RESERVE_SPACE(4 + create->u.symlink->len);
-               WRITE32(create->u.symlink->len);
-               WRITEMEM(create->u.symlink->name, create->u.symlink->len);
+               RESERVE_SPACE(4);
+               WRITE32(create->u.symlink.len);
+               xdr_write_pages(xdr, create->u.symlink.pages, 0, create->u.symlink.len);
                break;
 
        case NF4BLK: case NF4CHR:
@@ -1160,7 +1160,7 @@ static int encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, con
        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;
 
@@ -1246,7 +1246,7 @@ static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclien
        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;
 
@@ -1945,7 +1945,7 @@ static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, uint32_t *p, const str
 /*
  * 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 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
  */
-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 compound_hdr hdr = {
@@ -2127,12 +2127,12 @@ static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
        }
        READ32(nfserr);
        if (nfserr != NFS_OK)
-               return -nfs_stat_to_errno(nfserr);
+               return -nfs4_stat_to_errno(nfserr);
        return 0;
 }
 
 /* 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;
        unsigned int strlen;
@@ -2636,7 +2636,7 @@ static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t
        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;
 
@@ -2660,7 +2660,7 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, struct nf
        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;
 
@@ -3051,9 +3051,9 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, cons
        fattr->mode |= fmode;
        if ((status = decode_attr_nlink(xdr, bitmap, &fattr->nlink)) != 0)
                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;
-       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;
        if ((status = decode_attr_rdev(xdr, bitmap, &fattr->rdev)) != 0)
                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)
                                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)
@@ -3565,7 +3565,7 @@ static int decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res)
        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 opnum;
@@ -3598,7 +3598,7 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_client *clp)
                READ_BUF(len);
                return -NFSERR_CLID_INUSE;
        } else
-               return -nfs_stat_to_errno(nfserr);
+               return -nfs4_stat_to_errno(nfserr);
 
        return 0;
 }
@@ -4256,7 +4256,7 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, uint32_t *p, struct nfs_fsi
        if (!status)
                status = decode_fsinfo(&xdr, fsinfo);
        if (!status)
-               status = -nfs_stat_to_errno(hdr.status);
+               status = -nfs4_stat_to_errno(hdr.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
  */
 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 compound_hdr hdr;
@@ -4346,7 +4346,7 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, uint32_t *p,
        if (!status)
                status = decode_setclientid(&xdr, clp);
        if (!status)
-               status = -nfs_stat_to_errno(hdr.status);
+               status = -nfs4_stat_to_errno(hdr.status);
        return status;
 }
 
@@ -4368,7 +4368,7 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, s
        if (!status)
                status = decode_fsinfo(&xdr, fsinfo);
        if (!status)
-               status = -nfs_stat_to_errno(hdr.status);
+               status = -nfs4_stat_to_errno(hdr.status);
        return status;
 }
 
@@ -4521,7 +4521,7 @@ static struct {
  * This one is used jointly by NFSv2 and NFSv3.
  */
 static int
-nfs_stat_to_errno(int stat)
+nfs4_stat_to_errno(int stat)
 {
        int i;
        for (i = 0; nfs_errtbl[i].stat != -1; i++) {
index b3899ea..630e506 100644 (file)
@@ -66,14 +66,14 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
 
        dprintk("%s: call getattr\n", __FUNCTION__);
        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);
        if (status)
                return status;
        dprintk("%s: call statfs\n", __FUNCTION__);
        msg.rpc_proc = &nfs_procedures[NFSPROC_STATFS];
        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);
        if (status)
                return status;
@@ -425,16 +425,17 @@ nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
 }
 
 static int
-nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
-                struct iattr *sattr, struct nfs_fh *fhandle,
-                struct nfs_fattr *fattr)
+nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
+                unsigned int len, struct iattr *sattr)
 {
+       struct nfs_fh fhandle;
+       struct nfs_fattr fattr;
        struct nfs_symlinkargs  arg = {
                .fromfh         = NFS_FH(dir),
-               .fromname       = name->name,
-               .fromlen        = name->len,
-               .topath         = path->name,
-               .tolen          = path->len,
+               .fromname       = dentry->d_name.name,
+               .fromlen        = dentry->d_name.len,
+               .pages          = &page,
+               .pathlen        = len,
                .sattr          = sattr
        };
        struct rpc_message msg = {
@@ -443,13 +444,25 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
        };
        int                     status;
 
-       if (path->len > NFS2_MAXPATHLEN)
+       if (len > NFS2_MAXPATHLEN)
                return -ENAMETOOLONG;
-       dprintk("NFS call  symlink %s -> %s\n", name->name, path->name);
-       nfs_fattr_init(fattr);
-       fhandle->size = 0;
+
+       dprintk("NFS call  symlink %s\n", dentry->d_name.name);
+
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
        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);
        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 */
        .dentry_ops     = &nfs_dentry_operations,
        .dir_inode_ops  = &nfs_dir_inode_operations,
index f0aff82..69f1549 100644 (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;
 
                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,
                        (long long)NFS_FILEID(inode),
                        (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);
 
-       /* Is this a short read? */
-       if (task->tk_status >= 0 && resp->count < argp->count && !resp->eof) {
+       if (task->tk_status < 0) {
+               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);
                /* Has the server at least made some progress? */
                if (resp->count != 0) {
@@ -616,6 +621,10 @@ int nfs_readpage(struct file *file, struct page *page)
        if (error)
                goto out_error;
 
+       error = -ESTALE;
+       if (NFS_STALE(inode))
+               goto out_error;
+
        if (file == NULL) {
                ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
                if (ctx == NULL)
@@ -678,7 +687,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
        };
        struct inode *inode = mapping->host;
        struct nfs_server *server = NFS_SERVER(inode);
-       int ret;
+       int ret = -ESTALE;
 
        dprintk("NFS: nfs_readpages (%s/%Ld %d)\n",
                        inode->i_sb->s_id,
@@ -686,6 +695,9 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
                        nr_pages);
        nfs_inc_stats(inode, NFSIOS_VFSREADPAGES);
 
+       if (NFS_STALE(inode))
+               goto out;
+
        if (filp == NULL) {
                desc.ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
                if (desc.ctx == NULL)
@@ -701,6 +713,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
                        ret = err;
        }
        put_nfs_open_context(desc.ctx);
+out:
        return ret;
 }
 
index e8a9bee..b99113b 100644 (file)
  *
  *  Split from inode.c by David Howells <dhowells@redhat.com>
  *
+ * - superblocks are indexed on server only - all inodes, dentries, etc. associated with a
+ *   particular server are held in the same superblock
+ * - NFS superblocks can have several effective roots to the dentry tree
+ * - directory type roots are spliced into the tree when a path from one root reaches the root
+ *   of another (see nfs_lookup())
  */
 
 #include <linux/config.h>
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
-/* 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)
-
-/*
- * RPC cruft for NFS
- */
-static struct rpc_version * nfs_version[] = {
-       NULL,
-       NULL,
-       &nfs_version2,
-#if defined(CONFIG_NFS_V3)
-       &nfs_version3,
-#elif defined(CONFIG_NFS_V4)
-       NULL,
-#endif
-#if defined(CONFIG_NFS_V4)
-       &nfs_version4,
-#endif
-};
-
-static struct rpc_program nfs_program = {
-       .name                   = "nfs",
-       .number                 = NFS_PROGRAM,
-       .nrvers                 = ARRAY_SIZE(nfs_version),
-       .version                = nfs_version,
-       .stats                  = &nfs_rpcstat,
-       .pipe_dir_name          = "/nfs",
-};
-
-struct rpc_stat nfs_rpcstat = {
-       .program                = &nfs_program
-};
-
-
-#ifdef CONFIG_NFS_V3_ACL
-static struct rpc_stat         nfsacl_rpcstat = { &nfsacl_program };
-static struct rpc_version *    nfsacl_version[] = {
-       [3]                     = &nfsacl_version3,
-};
-
-struct rpc_program             nfsacl_program = {
-       .name =                 "nfsacl",
-       .number =               NFS_ACL_PROGRAM,
-       .nrvers =               ARRAY_SIZE(nfsacl_version),
-       .version =              nfsacl_version,
-       .stats =                &nfsacl_rpcstat,
-};
-#endif  /* CONFIG_NFS_V3_ACL */
-
 static void nfs_umount_begin(struct vfsmount *, int);
 static int  nfs_statfs(struct dentry *, struct kstatfs *);
 static int  nfs_show_options(struct seq_file *, struct vfsmount *);
 static int  nfs_show_stats(struct seq_file *, struct vfsmount *);
 static int nfs_get_sb(struct file_system_type *, int, const char *, void *, struct vfsmount *);
-static int nfs_clone_nfs_sb(struct file_system_type *fs_type,
+static int nfs_xdev_get_sb(struct file_system_type *fs_type,
                int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
 static void nfs_kill_super(struct super_block *);
 
@@ -123,10 +74,10 @@ static struct file_system_type nfs_fs_type = {
        .fs_flags       = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
 };
 
-struct file_system_type clone_nfs_fs_type = {
+struct file_system_type nfs_xdev_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "nfs",
-       .get_sb         = nfs_clone_nfs_sb,
+       .get_sb         = nfs_xdev_get_sb,
        .kill_sb        = nfs_kill_super,
        .fs_flags       = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
 };
@@ -145,10 +96,10 @@ static struct super_operations nfs_sops = {
 #ifdef CONFIG_NFS_V4
 static int nfs4_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
-static int nfs_clone_nfs4_sb(struct file_system_type *fs_type,
-               int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
-static int nfs_referral_nfs4_sb(struct file_system_type *fs_type,
-               int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
+static int nfs4_xdev_get_sb(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
+static int nfs4_referral_get_sb(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
 static void nfs4_kill_super(struct super_block *sb);
 
 static struct file_system_type nfs4_fs_type = {
@@ -159,18 +110,18 @@ static struct file_system_type nfs4_fs_type = {
        .fs_flags       = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
 };
 
-struct file_system_type clone_nfs4_fs_type = {
+struct file_system_type nfs4_xdev_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "nfs4",
-       .get_sb         = nfs_clone_nfs4_sb,
+       .get_sb         = nfs4_xdev_get_sb,
        .kill_sb        = nfs4_kill_super,
        .fs_flags       = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
 };
 
-struct file_system_type nfs_referral_nfs4_fs_type = {
+struct file_system_type nfs4_referral_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "nfs4",
-       .get_sb         = nfs_referral_nfs4_sb,
+       .get_sb         = nfs4_referral_get_sb,
        .kill_sb        = nfs4_kill_super,
        .fs_flags       = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
 };
@@ -187,39 +138,7 @@ static struct super_operations nfs4_sops = {
 };
 #endif
 
-#ifdef CONFIG_NFS_V4
-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);
-#endif
-
-#ifdef CONFIG_NFS_V4
-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);
-#endif
+static struct shrinker *acl_shrinker;
 
 /*
  * Register the NFS filesystems
@@ -240,6 +159,7 @@ int __init register_nfs_fs(void)
        if (ret < 0)
                goto error_2;
 #endif
+       acl_shrinker = set_shrinker(DEFAULT_SEEKS, nfs_access_cache_shrinker);
        return 0;
 
 #ifdef CONFIG_NFS_V4
@@ -257,6 +177,8 @@ error_0:
  */
 void __exit unregister_nfs_fs(void)
 {
+       if (acl_shrinker != NULL)
+               remove_shrinker(acl_shrinker);
 #ifdef CONFIG_NFS_V4
        unregister_filesystem(&nfs4_fs_type);
        nfs_unregister_sysctl();
@@ -269,11 +191,10 @@ void __exit unregister_nfs_fs(void)
  */
 static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
-       struct super_block *sb = dentry->d_sb;
-       struct nfs_server *server = NFS_SB(sb);
+       struct nfs_server *server = NFS_SB(dentry->d_sb);
        unsigned char blockbits;
        unsigned long blockres;
-       struct nfs_fh *rootfh = NFS_FH(sb->s_root->d_inode);
+       struct nfs_fh *fh = NFS_FH(dentry->d_inode);
        struct nfs_fattr fattr;
        struct nfs_fsstat res = {
                        .fattr = &fattr,
@@ -282,7 +203,7 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
        lock_kernel();
 
-       error = server->rpc_ops->statfs(server, rootfh, &res);
+       error = server->nfs_client->rpc_ops->statfs(server, fh, &res);
        buf->f_type = NFS_SUPER_MAGIC;
        if (error < 0)
                goto out_err;
@@ -292,7 +213,7 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
         * case where f_frsize != f_bsize.  Eventually we want to
         * report the value of wtmult in this field.
         */
-       buf->f_frsize = sb->s_blocksize;
+       buf->f_frsize = dentry->d_sb->s_blocksize;
 
        /*
         * On most *nix systems, f_blocks, f_bfree, and f_bavail
@@ -301,8 +222,8 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
         * thus historically Linux's sys_statfs reports these
         * fields in units of f_bsize.
         */
-       buf->f_bsize = sb->s_blocksize;
-       blockbits = sb->s_blocksize_bits;
+       buf->f_bsize = dentry->d_sb->s_blocksize;
+       blockbits = dentry->d_sb->s_blocksize_bits;
        blockres = (1 << blockbits) - 1;
        buf->f_blocks = (res.tbytes + blockres) >> blockbits;
        buf->f_bfree = (res.fbytes + blockres) >> blockbits;
@@ -323,9 +244,12 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
 }
 
+/*
+ * Map the security flavour number to a name
+ */
 static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour)
 {
-       static struct {
+       static const struct {
                rpc_authflavor_t flavour;
                const char *str;
        } sec_flavours[] = {
@@ -356,10 +280,10 @@ static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour)
  */
 static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults)
 {
-       static struct proc_nfs_info {
+       static const struct proc_nfs_info {
                int flag;
-               char *str;
-               char *nostr;
+               const char *str;
+               const char *nostr;
        } nfs_info[] = {
                { NFS_MOUNT_SOFT, ",soft", ",hard" },
                { NFS_MOUNT_INTR, ",intr", "" },
@@ -369,11 +293,12 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
                { NFS_MOUNT_NOACL, ",noacl", "" },
                { 0, NULL, NULL }
        };
-       struct proc_nfs_info *nfs_infop;
+       const struct proc_nfs_info *nfs_infop;
+       struct nfs_client *clp = nfss->nfs_client;
        char buf[12];
-       char *proto;
+       const char *proto;
 
-       seq_printf(m, ",vers=%d", nfss->rpc_ops->version);
+       seq_printf(m, ",vers=%d", clp->rpc_ops->version);
        seq_printf(m, ",rsize=%d", nfss->rsize);
        seq_printf(m, ",wsize=%d", nfss->wsize);
        if (nfss->acregmin != 3*HZ || showdefaults)
@@ -402,8 +327,8 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
                        proto = buf;
        }
        seq_printf(m, ",proto=%s", proto);
-       seq_printf(m, ",timeo=%lu", 10U * nfss->retrans_timeo / HZ);
-       seq_printf(m, ",retrans=%u", nfss->retrans_count);
+       seq_printf(m, ",timeo=%lu", 10U * clp->retrans_timeo / HZ);
+       seq_printf(m, ",retrans=%u", clp->retrans_count);
        seq_printf(m, ",sec=%s", nfs_pseudoflavour_to_name(nfss->client->cl_auth->au_flavor));
 }
 
@@ -417,7 +342,7 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
        nfs_show_mount_options(m, nfss, 0);
 
        seq_puts(m, ",addr=");
-       seq_escape(m, nfss->hostname, " \t\n\\");
+       seq_escape(m, nfss->nfs_client->cl_hostname, " \t\n\\");
 
        return 0;
 }
@@ -454,7 +379,7 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
        seq_printf(m, ",namelen=%d", nfss->namelen);
 
 #ifdef CONFIG_NFS_V4
-       if (nfss->rpc_ops->version == 4) {
+       if (nfss->nfs_client->cl_nfsversion == 4) {
                seq_printf(m, "\n\tnfsv4:\t");
                seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
                seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
@@ -501,782 +426,353 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
 
 /*
  * Begin unmount by attempting to remove all automounted mountpoints we added
- * in response to traversals
+ * in response to xdev traversals and referrals
  */
 static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags)
 {
-       struct nfs_server *server;
-       struct rpc_clnt *rpc;
-
        shrink_submounts(vfsmnt, &nfs_automount_list);
-       if (!(flags & MNT_FORCE))
-               return;
-       /* -EIO all pending I/O */
-       server = NFS_SB(vfsmnt->mnt_sb);
-       rpc = server->client;
-       if (!IS_ERR(rpc))
-               rpc_killall_tasks(rpc);
-       rpc = server->client_acl;
-       if (!IS_ERR(rpc))
-               rpc_killall_tasks(rpc);
 }
 
 /*
- * Obtain the root inode of the file system.
+ * Validate the NFS2/NFS3 mount data
+ * - fills in the mount root filehandle
  */
-static struct inode *
-nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *fsinfo)
+static int nfs_validate_mount_data(struct nfs_mount_data *data,
+                                  struct nfs_fh *mntfh)
 {
-       struct nfs_server       *server = NFS_SB(sb);
-       int                     error;
-
-       error = server->rpc_ops->getroot(server, rootfh, fsinfo);
-       if (error < 0) {
-               dprintk("nfs_get_root: getattr error = %d\n", -error);
-               return ERR_PTR(error);
+       if (data == NULL) {
+               dprintk("%s: missing data argument\n", __FUNCTION__);
+               return -EINVAL;
        }
 
-       server->fsid = fsinfo->fattr->fsid;
-       return nfs_fhget(sb, rootfh, fsinfo->fattr);
-}
-
-/*
- * Do NFS version-independent mount processing, and sanity checking
- */
-static int
-nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
-{
-       struct nfs_server       *server;
-       struct inode            *root_inode;
-       struct nfs_fattr        fattr;
-       struct nfs_fsinfo       fsinfo = {
-                                       .fattr = &fattr,
-                               };
-       struct nfs_pathconf pathinfo = {
-                       .fattr = &fattr,
-       };
-       int no_root_error = 0;
-       unsigned long max_rpc_payload;
-
-       /* We probably want something more informative here */
-       snprintf(sb->s_id, sizeof(sb->s_id), "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev));
-
-       server = NFS_SB(sb);
+       if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) {
+               dprintk("%s: bad mount version\n", __FUNCTION__);
+               return -EINVAL;
+       }
 
-       sb->s_magic      = NFS_SUPER_MAGIC;
+       switch (data->version) {
+               case 1:
+                       data->namlen = 0;
+               case 2:
+                       data->bsize  = 0;
+               case 3:
+                       if (data->flags & NFS_MOUNT_VER3) {
+                               dprintk("%s: mount structure version %d does not support NFSv3\n",
+                                               __FUNCTION__,
+                                               data->version);
+                               return -EINVAL;
+                       }
+                       data->root.size = NFS2_FHSIZE;
+                       memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
+               case 4:
+                       if (data->flags & NFS_MOUNT_SECFLAVOUR) {
+                               dprintk("%s: mount structure version %d does not support strong security\n",
+                                               __FUNCTION__,
+                                               data->version);
+                               return -EINVAL;
+                       }
+               case 5:
+                       memset(data->context, 0, sizeof(data->context));
+       }
 
-       server->io_stats = nfs_alloc_iostats();
-       if (server->io_stats == NULL)
-               return -ENOMEM;
+       /* Set the pseudoflavor */
+       if (!(data->flags & NFS_MOUNT_SECFLAVOUR))
+               data->pseudoflavor = RPC_AUTH_UNIX;
 
-       root_inode = nfs_get_root(sb, &server->fh, &fsinfo);
-       /* Did getting the root inode fail? */
-       if (IS_ERR(root_inode)) {
-               no_root_error = PTR_ERR(root_inode);
-               goto out_no_root;
-       }
-       sb->s_root = d_alloc_root(root_inode);
-       if (!sb->s_root) {
-               no_root_error = -ENOMEM;
-               goto out_no_root;
+#ifndef CONFIG_NFS_V3
+       /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */
+       if (data->flags & NFS_MOUNT_VER3) {
+               dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__);
+               return -EPROTONOSUPPORT;
        }
-       sb->s_root->d_op = server->rpc_ops->dentry_ops;
-
-       /* mount time stamp, in seconds */
-       server->mount_time = jiffies;
-
-       /* Get some general file system info */
-       if (server->namelen == 0 &&
-           server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0)
-               server->namelen = pathinfo.max_namelen;
-       /* Work out a lot of parameters */
-       if (server->rsize == 0)
-               server->rsize = nfs_block_size(fsinfo.rtpref, NULL);
-       if (server->wsize == 0)
-               server->wsize = nfs_block_size(fsinfo.wtpref, NULL);
-
-       if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax)
-               server->rsize = nfs_block_size(fsinfo.rtmax, NULL);
-       if (fsinfo.wtmax >= 512 && server->wsize > fsinfo.wtmax)
-               server->wsize = nfs_block_size(fsinfo.wtmax, NULL);
-
-       max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL);
-       if (server->rsize > max_rpc_payload)
-               server->rsize = max_rpc_payload;
-       if (server->rsize > NFS_MAX_FILE_IO_SIZE)
-               server->rsize = NFS_MAX_FILE_IO_SIZE;
-       server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-
-       if (server->wsize > max_rpc_payload)
-               server->wsize = max_rpc_payload;
-       if (server->wsize > NFS_MAX_FILE_IO_SIZE)
-               server->wsize = NFS_MAX_FILE_IO_SIZE;
-       server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+#endif /* CONFIG_NFS_V3 */
 
-       if (sb->s_blocksize == 0)
-               sb->s_blocksize = nfs_block_bits(server->wsize,
-                                                        &sb->s_blocksize_bits);
-       server->wtmult = nfs_block_bits(fsinfo.wtmult, NULL);
-
-       server->dtsize = nfs_block_size(fsinfo.dtpref, NULL);
-       if (server->dtsize > PAGE_CACHE_SIZE)
-               server->dtsize = PAGE_CACHE_SIZE;
-       if (server->dtsize > server->rsize)
-               server->dtsize = server->rsize;
-
-       if (server->flags & NFS_MOUNT_NOAC) {
-               server->acregmin = server->acregmax = 0;
-               server->acdirmin = server->acdirmax = 0;
-               sb->s_flags |= MS_SYNCHRONOUS;
+       /* We now require that the mount process passes the remote address */
+       if (data->addr.sin_addr.s_addr == INADDR_ANY) {
+               dprintk("%s: mount program didn't pass remote address!\n",
+                       __FUNCTION__);
+               return -EINVAL;
        }
-       server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD;
 
-       nfs_super_set_maxbytes(sb, fsinfo.maxfilesize);
+       /* Prepare the root filehandle */
+       if (data->flags & NFS_MOUNT_VER3)
+               mntfh->size = data->root.size;
+       else
+               mntfh->size = NFS2_FHSIZE;
+
+       if (mntfh->size > sizeof(mntfh->data)) {
+               dprintk("%s: invalid root filehandle\n", __FUNCTION__);
+               return -EINVAL;
+       }
 
-       server->client->cl_intr = (server->flags & NFS_MOUNT_INTR) ? 1 : 0;
-       server->client->cl_softrtry = (server->flags & NFS_MOUNT_SOFT) ? 1 : 0;
+       memcpy(mntfh->data, data->root.data, mntfh->size);
+       if (mntfh->size < sizeof(mntfh->data))
+               memset(mntfh->data + mntfh->size, 0,
+                      sizeof(mntfh->data) - mntfh->size);
 
-       /* We're airborne Set socket buffersize */
-       rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
        return 0;
-       /* Yargs. It didn't work out. */
-out_no_root:
-       dprintk("nfs_sb_init: get root inode failed: errno %d\n", -no_root_error);
-       if (!IS_ERR(root_inode))
-               iput(root_inode);
-       return no_root_error;
 }
 
 /*
- * Initialise the timeout values for a connection
+ * Initialise the common bits of the superblock
  */
-static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, unsigned int timeo, unsigned int retrans)
+static inline void nfs_initialise_sb(struct super_block *sb)
 {
-       to->to_initval = timeo * HZ / 10;
-       to->to_retries = retrans;
-       if (!to->to_retries)
-               to->to_retries = 2;
-
-       switch (proto) {
-       case IPPROTO_TCP:
-               if (!to->to_initval)
-                       to->to_initval = 60 * HZ;
-               if (to->to_initval > NFS_MAX_TCP_TIMEOUT)
-                       to->to_initval = NFS_MAX_TCP_TIMEOUT;
-               to->to_increment = to->to_initval;
-               to->to_maxval = to->to_initval + (to->to_increment * to->to_retries);
-               to->to_exponential = 0;
-               break;
-       case IPPROTO_UDP:
-       default:
-               if (!to->to_initval)
-                       to->to_initval = 11 * HZ / 10;
-               if (to->to_initval > NFS_MAX_UDP_TIMEOUT)
-                       to->to_initval = NFS_MAX_UDP_TIMEOUT;
-               to->to_maxval = NFS_MAX_UDP_TIMEOUT;
-               to->to_exponential = 1;
-               break;
-       }
-}
+       struct nfs_server *server = NFS_SB(sb);
 
-/*
- * Create an RPC client handle.
- */
-static struct rpc_clnt *
-nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data)
-{
-       struct rpc_timeout      timeparms;
-       struct rpc_xprt         *xprt = NULL;
-       struct rpc_clnt         *clnt = NULL;
-       int                     proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
-
-       nfs_init_timeout_values(&timeparms, proto, data->timeo, data->retrans);
-
-       server->retrans_timeo = timeparms.to_initval;
-       server->retrans_count = timeparms.to_retries;
-
-       /* create transport and client */
-       xprt = xprt_create_proto(proto, &server->addr, &timeparms);
-       if (IS_ERR(xprt)) {
-               dprintk("%s: cannot create RPC transport. Error = %ld\n",
-                               __FUNCTION__, PTR_ERR(xprt));
-               return (struct rpc_clnt *)xprt;
-       }
-       clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
-                                server->rpc_ops->version, data->pseudoflavor);
-       if (IS_ERR(clnt)) {
-               dprintk("%s: cannot create RPC client. Error = %ld\n",
-                               __FUNCTION__, PTR_ERR(xprt));
-               goto out_fail;
-       }
+       sb->s_magic = NFS_SUPER_MAGIC;
 
-       clnt->cl_intr     = 1;
-       clnt->cl_softrtry = 1;
+       /* We probably want something more informative here */
+       snprintf(sb->s_id, sizeof(sb->s_id),
+                "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev));
+
+       if (sb->s_blocksize == 0)
+               sb->s_blocksize = nfs_block_bits(server->wsize,
+                                                &sb->s_blocksize_bits);
 
-       return clnt;
+       if (server->flags & NFS_MOUNT_NOAC)
+               sb->s_flags |= MS_SYNCHRONOUS;
 
-out_fail:
-       return clnt;
+       nfs_super_set_maxbytes(sb, server->maxfilesize);
 }
 
 /*
- * Clone a server record
+ * Finish setting up an NFS2/3 superblock
  */
-static struct nfs_server *nfs_clone_server(struct super_block *sb, struct nfs_clone_mount *data)
+static void nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data)
 {
        struct nfs_server *server = NFS_SB(sb);
-       struct nfs_server *parent = NFS_SB(data->sb);
-       struct inode *root_inode;
-       struct nfs_fsinfo fsinfo;
-       void *err = ERR_PTR(-ENOMEM);
-
-       sb->s_op = data->sb->s_op;
-       sb->s_blocksize = data->sb->s_blocksize;
-       sb->s_blocksize_bits = data->sb->s_blocksize_bits;
-       sb->s_maxbytes = data->sb->s_maxbytes;
-
-       server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
-       server->io_stats = nfs_alloc_iostats();
-       if (server->io_stats == NULL)
-               goto out;
-
-       server->client = rpc_clone_client(parent->client);
-       if (IS_ERR((err = server->client)))
-               goto out;
-
-       if (!IS_ERR(parent->client_sys)) {
-               server->client_sys = rpc_clone_client(parent->client_sys);
-               if (IS_ERR((err = server->client_sys)))
-                       goto out;
-       }
-       if (!IS_ERR(parent->client_acl)) {
-               server->client_acl = rpc_clone_client(parent->client_acl);
-               if (IS_ERR((err = server->client_acl)))
-                       goto out;
-       }
-       root_inode = nfs_fhget(sb, data->fh, data->fattr);
-       if (!root_inode)
-               goto out;
-       sb->s_root = d_alloc_root(root_inode);
-       if (!sb->s_root)
-               goto out_put_root;
-       fsinfo.fattr = data->fattr;
-       if (NFS_PROTO(root_inode)->fsinfo(server, data->fh, &fsinfo) == 0)
-               nfs_super_set_maxbytes(sb, fsinfo.maxfilesize);
-       sb->s_root->d_op = server->rpc_ops->dentry_ops;
-       sb->s_flags |= MS_ACTIVE;
-       return server;
-out_put_root:
-       iput(root_inode);
-out:
-       return err;
-}
 
-/*
- * Copy an existing superblock and attach revised data
- */
-static int nfs_clone_generic_sb(struct nfs_clone_mount *data,
-               struct super_block *(*fill_sb)(struct nfs_server *, struct nfs_clone_mount *),
-               struct nfs_server *(*fill_server)(struct super_block *, struct nfs_clone_mount *),
-               struct vfsmount *mnt)
-{
-       struct nfs_server *server;
-       struct nfs_server *parent = NFS_SB(data->sb);
-       struct super_block *sb = ERR_PTR(-EINVAL);
-       char *hostname;
-       int error = -ENOMEM;
-       int len;
-
-       server = kmalloc(sizeof(struct nfs_server), GFP_KERNEL);
-       if (server == NULL)
-               goto out_err;
-       memcpy(server, parent, sizeof(*server));
-       hostname = (data->hostname != NULL) ? data->hostname : parent->hostname;
-       len = strlen(hostname) + 1;
-       server->hostname = kmalloc(len, GFP_KERNEL);
-       if (server->hostname == NULL)
-               goto free_server;
-       memcpy(server->hostname, hostname, len);
-       error = rpciod_up();
-       if (error != 0)
-               goto free_hostname;
-
-       sb = fill_sb(server, data);
-       if (IS_ERR(sb)) {
-               error = PTR_ERR(sb);
-               goto kill_rpciod;
-       }
-               
-       if (sb->s_root)
-               goto out_rpciod_down;
+       sb->s_blocksize_bits = 0;
+       sb->s_blocksize = 0;
+       if (data->bsize)
+               sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
 
-       server = fill_server(sb, data);
-       if (IS_ERR(server)) {
-               error = PTR_ERR(server);
-               goto out_deactivate;
+       if (server->flags & NFS_MOUNT_VER3) {
+               /* The VFS shouldn't apply the umask to mode bits. We will do
+                * so ourselves when necessary.
+                */
+               sb->s_flags |= MS_POSIXACL;
+               sb->s_time_gran = 1;
        }
-       return simple_set_mnt(mnt, sb);
-out_deactivate:
-       up_write(&sb->s_umount);
-       deactivate_super(sb);
-       return error;
-out_rpciod_down:
-       rpciod_down();
-       kfree(server->hostname);
-       kfree(server);
-       return simple_set_mnt(mnt, sb);
-kill_rpciod:
-       rpciod_down();
-free_hostname:
-       kfree(server->hostname);
-free_server:
-       kfree(server);
-out_err:
-       return error;
+
+       sb->s_op = &nfs_sops;
+       nfs_initialise_sb(sb);
 }
 
 /*
- * Set up an NFS2/3 superblock
- *
- * The way this works is that the mount process passes a structure
- * in the data argument which contains the server's IP address
- * and the root file handle obtained from the server's mount
- * daemon. We stash these away in the private superblock fields.
+ * Finish setting up a cloned NFS2/3 superblock
  */
-static int
-nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
+static void nfs_clone_super(struct super_block *sb,
+                           const struct super_block *old_sb)
 {
-       struct nfs_server       *server;
-       rpc_authflavor_t        authflavor;
+       struct nfs_server *server = NFS_SB(sb);
 
-       server           = NFS_SB(sb);
-       sb->s_blocksize_bits = 0;
-       sb->s_blocksize = 0;
-       if (data->bsize)
-               sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
-       if (data->rsize)
-               server->rsize = nfs_block_size(data->rsize, NULL);
-       if (data->wsize)
-               server->wsize = nfs_block_size(data->wsize, NULL);
-       server->flags    = data->flags & NFS_MOUNT_FLAGMASK;
-
-       server->acregmin = data->acregmin*HZ;
-       server->acregmax = data->acregmax*HZ;
-       server->acdirmin = data->acdirmin*HZ;
-       server->acdirmax = data->acdirmax*HZ;
-
-       /* Start lockd here, before we might error out */
-       if (!(server->flags & NFS_MOUNT_NONLM))
-               lockd_up();
-
-       server->namelen  = data->namlen;
-       server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
-       if (!server->hostname)
-               return -ENOMEM;
-       strcpy(server->hostname, data->hostname);
-
-       /* Check NFS protocol revision and initialize RPC op vector
-        * and file handle pool. */
-#ifdef CONFIG_NFS_V3
-       if (server->flags & NFS_MOUNT_VER3) {
-               server->rpc_ops = &nfs_v3_clientops;
-               server->caps |= NFS_CAP_READDIRPLUS;
-       } else {
-               server->rpc_ops = &nfs_v2_clientops;
-       }
-#else
-       server->rpc_ops = &nfs_v2_clientops;
-#endif
+       sb->s_blocksize_bits = old_sb->s_blocksize_bits;
+       sb->s_blocksize = old_sb->s_blocksize;
+       sb->s_maxbytes = old_sb->s_maxbytes;
 
-       /* Fill in pseudoflavor for mount version < 5 */
-       if (!(data->flags & NFS_MOUNT_SECFLAVOUR))
-               data->pseudoflavor = RPC_AUTH_UNIX;
-       authflavor = data->pseudoflavor;        /* save for sb_init() */
-       /* XXX maybe we want to add a server->pseudoflavor field */
-
-       /* Create RPC client handles */
-       server->client = nfs_create_client(server, data);
-       if (IS_ERR(server->client))
-               return PTR_ERR(server->client);
-       /* RFC 2623, sec 2.3.2 */
-       if (authflavor != RPC_AUTH_UNIX) {
-               struct rpc_auth *auth;
-
-               server->client_sys = rpc_clone_client(server->client);
-               if (IS_ERR(server->client_sys))
-                       return PTR_ERR(server->client_sys);
-               auth = rpcauth_create(RPC_AUTH_UNIX, server->client_sys);
-               if (IS_ERR(auth))
-                       return PTR_ERR(auth);
-       } else {
-               atomic_inc(&server->client->cl_count);
-               server->client_sys = server->client;
-       }
        if (server->flags & NFS_MOUNT_VER3) {
-#ifdef CONFIG_NFS_V3_ACL
-               if (!(server->flags & NFS_MOUNT_NOACL)) {
-                       server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3);
-                       /* No errors! Assume that Sun nfsacls are supported */
-                       if (!IS_ERR(server->client_acl))
-                               server->caps |= NFS_CAP_ACLS;
-               }
-#else
-               server->flags &= ~NFS_MOUNT_NOACL;
-#endif /* CONFIG_NFS_V3_ACL */
-               /*
-                * The VFS shouldn't apply the umask to mode bits. We will
-                * do so ourselves when necessary.
+               /* The VFS shouldn't apply the umask to mode bits. We will do
+                * so ourselves when necessary.
                 */
                sb->s_flags |= MS_POSIXACL;
-               if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
-                       server->namelen = NFS3_MAXNAMLEN;
                sb->s_time_gran = 1;
-       } else {
-               if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
-                       server->namelen = NFS2_MAXNAMLEN;
        }
 
-       sb->s_op = &nfs_sops;
-       return nfs_sb_init(sb, authflavor);
+       sb->s_op = old_sb->s_op;
+       nfs_initialise_sb(sb);
 }
 
-static int nfs_set_super(struct super_block *s, void *data)
+static int nfs_set_super(struct super_block *s, void *_server)
 {
-       s->s_fs_info = data;
-       return set_anon_super(s, data);
+       struct nfs_server *server = _server;
+       int ret;
+
+       s->s_fs_info = server;
+       ret = set_anon_super(s, server);
+       if (ret == 0)
+               server->s_dev = s->s_dev;
+       return ret;
 }
 
 static int nfs_compare_super(struct super_block *sb, void *data)
 {
-       struct nfs_server *server = data;
-       struct nfs_server *old = NFS_SB(sb);
+       struct nfs_server *server = data, *old = NFS_SB(sb);
 
-       if (old->addr.sin_addr.s_addr != server->addr.sin_addr.s_addr)
+       if (old->nfs_client != server->nfs_client)
                return 0;
-       if (old->addr.sin_port != server->addr.sin_port)
+       if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0)
                return 0;
-       return !nfs_compare_fh(&old->fh, &server->fh);
+       return 1;
 }
 
 static int nfs_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
 {
-       int error;
        struct nfs_server *server = NULL;
        struct super_block *s;
-       struct nfs_fh *root;
+       struct nfs_fh mntfh;
        struct nfs_mount_data *data = raw_data;
+       struct dentry *mntroot;
+       int error;
 
-       error = -EINVAL;
-       if (data == NULL) {
-               dprintk("%s: missing data argument\n", __FUNCTION__);
-               goto out_err_noserver;
-       }
-       if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) {
-               dprintk("%s: bad mount version\n", __FUNCTION__);
-               goto out_err_noserver;
-       }
-       switch (data->version) {
-               case 1:
-                       data->namlen = 0;
-               case 2:
-                       data->bsize  = 0;
-               case 3:
-                       if (data->flags & NFS_MOUNT_VER3) {
-                               dprintk("%s: mount structure version %d does not support NFSv3\n",
-                                               __FUNCTION__,
-                                               data->version);
-                               goto out_err_noserver;
-                       }
-                       data->root.size = NFS2_FHSIZE;
-                       memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
-               case 4:
-                       if (data->flags & NFS_MOUNT_SECFLAVOUR) {
-                               dprintk("%s: mount structure version %d does not support strong security\n",
-                                               __FUNCTION__,
-                                               data->version);
-                               goto out_err_noserver;
-                       }
-               case 5:
-                       memset(data->context, 0, sizeof(data->context));
-       }
-#ifndef CONFIG_NFS_V3
-       /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */
-       error = -EPROTONOSUPPORT;
-       if (data->flags & NFS_MOUNT_VER3) {
-               dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__);
-               goto out_err_noserver;
-       }
-#endif /* CONFIG_NFS_V3 */
+       /* Validate the mount data */
+       error = nfs_validate_mount_data(data, &mntfh);
+       if (error < 0)
+               return error;
 
-       error = -ENOMEM;
-       server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
-       if (!server)
+       /* Get a volume representation */
+       server = nfs_create_server(data, &mntfh);
+       if (IS_ERR(server)) {
+               error = PTR_ERR(server);
                goto out_err_noserver;
-       /* Zero out the NFS state stuff */
-       init_nfsv4_state(server);
-       server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
-
-       root = &server->fh;
-       if (data->flags & NFS_MOUNT_VER3)
-               root->size = data->root.size;
-       else
-               root->size = NFS2_FHSIZE;
-       error = -EINVAL;
-       if (root->size > sizeof(root->data)) {
-               dprintk("%s: invalid root filehandle\n", __FUNCTION__);
-               goto out_err;
-       }
-       memcpy(root->data, data->root.data, root->size);
-
-       /* We now require that the mount process passes the remote address */
-       memcpy(&server->addr, &data->addr, sizeof(server->addr));
-       if (server->addr.sin_addr.s_addr == INADDR_ANY) {
-               dprintk("%s: mount program didn't pass remote address!\n",
-                               __FUNCTION__);
-               goto out_err;
-       }
-
-       /* Fire up rpciod if not yet running */
-       error = rpciod_up();
-       if (error < 0) {
-               dprintk("%s: couldn't start rpciod! Error = %d\n",
-                               __FUNCTION__, error);
-               goto out_err;
        }
 
+       /* Get a superblock - note that we may end up sharing one that already exists */
        s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
        if (IS_ERR(s)) {
                error = PTR_ERR(s);
-               goto out_err_rpciod;
+               goto out_err_nosb;
        }
 
-       if (s->s_root)
-               goto out_rpciod_down;
+       if (s->s_fs_info != server) {
+               nfs_free_server(server);
+               server = NULL;
+       }
 
-       s->s_flags = flags;
+       if (!s->s_root) {
+               /* initial superblock/root creation */
+               s->s_flags = flags;
+               nfs_fill_super(s, data);
+       }
 
-       error = nfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
-       if (error) {
-               up_write(&s->s_umount);
-               deactivate_super(s);
-               return error;
+       mntroot = nfs_get_root(s, &mntfh);
+       if (IS_ERR(mntroot)) {
+               error = PTR_ERR(mntroot);
+               goto error_splat_super;
        }
-       s->s_flags |= MS_ACTIVE;
-       return simple_set_mnt(mnt, s);
 
-out_rpciod_down:
-       rpciod_down();
-       kfree(server);
-       return simple_set_mnt(mnt, s);
+       s->s_flags |= MS_ACTIVE;
+       mnt->mnt_sb = s;
+       mnt->mnt_root = mntroot;
+       return 0;
 
-out_err_rpciod:
-       rpciod_down();
-out_err:
-       kfree(server);
+out_err_nosb:
+       nfs_free_server(server);
 out_err_noserver:
        return error;
+
+error_splat_super:
+       up_write(&s->s_umount);
+       deactivate_super(s);
+       return error;
 }
 
+/*
+ * Destroy an NFS2/3 superblock
+ */
 static void nfs_kill_super(struct super_block *s)
 {
        struct nfs_server *server = NFS_SB(s);
 
        kill_anon_super(s);
-
-       if (!IS_ERR(server->client))
-               rpc_shutdown_client(server->client);
-       if (!IS_ERR(server->client_sys))
-               rpc_shutdown_client(server->client_sys);
-       if (!IS_ERR(server->client_acl))
-               rpc_shutdown_client(server->client_acl);
-
-       if (!(server->flags & NFS_MOUNT_NONLM))
-               lockd_down();   /* release rpc.lockd */
-
-       rpciod_down();          /* release rpciod */
-
-       nfs_free_iostats(server->io_stats);
-       kfree(server->hostname);
-       kfree(server);
-       nfs_release_automount_timer();
-}
-
-static struct super_block *nfs_clone_sb(struct nfs_server *server, struct nfs_clone_mount *data)
-{
-       struct super_block *sb;
-
-       server->fsid = data->fattr->fsid;
-       nfs_copy_fh(&server->fh, data->fh);
-       sb = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server);
-       if (!IS_ERR(sb) && sb->s_root == NULL && !(server->flags & NFS_MOUNT_NONLM))
-               lockd_up();
-       return sb;
+       nfs_free_server(server);
 }
 
-static int nfs_clone_nfs_sb(struct file_system_type *fs_type,
-               int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
+/*
+ * Clone an NFS2/3 server record on xdev traversal (FSID-change)
+ */
+static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags,
+                          const char *dev_name, void *raw_data,
+                          struct vfsmount *mnt)
 {
        struct nfs_clone_mount *data = raw_data;
-       return nfs_clone_generic_sb(data, nfs_clone_sb, nfs_clone_server, mnt);
-}
+       struct super_block *s;
+       struct nfs_server *server;
+       struct dentry *mntroot;
+       int error;
 
-#ifdef CONFIG_NFS_V4
-static struct rpc_clnt *nfs4_create_client(struct nfs_server *server,
-       struct rpc_timeout *timeparms, int proto, rpc_authflavor_t flavor)
-{
-       struct nfs4_client *clp;
-       struct rpc_xprt *xprt = NULL;
-       struct rpc_clnt *clnt = NULL;
-       int err = -EIO;
-
-       clp = nfs4_get_client(&server->addr.sin_addr);
-       if (!clp) {
-               dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__);
-               return ERR_PTR(err);
-       }
+       dprintk("--> nfs_xdev_get_sb()\n");
 
-       /* Now create transport and client */
-       down_write(&clp->cl_sem);
-       if (IS_ERR(clp->cl_rpcclient)) {
-               xprt = xprt_create_proto(proto, &server->addr, timeparms);
-               if (IS_ERR(xprt)) {
-                       up_write(&clp->cl_sem);
-                       err = PTR_ERR(xprt);
-                       dprintk("%s: cannot create RPC transport. Error = %d\n",
-                                       __FUNCTION__, err);
-                       goto out_fail;
-               }
-               /* Bind to a reserved port! */
-               xprt->resvport = 1;
-               clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
-                               server->rpc_ops->version, flavor);
-               if (IS_ERR(clnt)) {
-                       up_write(&clp->cl_sem);
-                       err = PTR_ERR(clnt);
-                       dprintk("%s: cannot create RPC client. Error = %d\n",
-                                       __FUNCTION__, err);
-                       goto out_fail;
-               }
-               clnt->cl_intr     = 1;
-               clnt->cl_softrtry = 1;
-               clp->cl_rpcclient = clnt;
-               memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr));
-               nfs_idmap_new(clp);
-       }
-       list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
-       clnt = rpc_clone_client(clp->cl_rpcclient);
-       if (!IS_ERR(clnt))
-               server->nfs4_state = clp;
-       up_write(&clp->cl_sem);
-       clp = NULL;
-
-       if (IS_ERR(clnt)) {
-               dprintk("%s: cannot create RPC client. Error = %d\n",
-                               __FUNCTION__, err);
-               return clnt;
+       /* create a new volume representation */
+       server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr);
+       if (IS_ERR(server)) {
+               error = PTR_ERR(server);
+               goto out_err_noserver;
        }
 
-       if (server->nfs4_state->cl_idmap == NULL) {
-               dprintk("%s: failed to create idmapper.\n", __FUNCTION__);
-               return ERR_PTR(-ENOMEM);
+       /* Get a superblock - note that we may end up sharing one that already exists */
+       s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server);
+       if (IS_ERR(s)) {
+               error = PTR_ERR(s);
+               goto out_err_nosb;
        }
 
-       if (clnt->cl_auth->au_flavor != flavor) {
-               struct rpc_auth *auth;
-
-               auth = rpcauth_create(flavor, clnt);
-               if (IS_ERR(auth)) {
-                       dprintk("%s: couldn't create credcache!\n", __FUNCTION__);
-                       return (struct rpc_clnt *)auth;
-               }
+       if (s->s_fs_info != server) {
+               nfs_free_server(server);
+               server = NULL;
        }
-       return clnt;
-
- out_fail:
-       if (clp)
-               nfs4_put_client(clp);
-       return ERR_PTR(err);
-}
-
-/*
- * Set up an NFS4 superblock
- */
-static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent)
-{
-       struct nfs_server *server;
-       struct rpc_timeout timeparms;
-       rpc_authflavor_t authflavour;
-       int err = -EIO;
 
-       sb->s_blocksize_bits = 0;
-       sb->s_blocksize = 0;
-       server = NFS_SB(sb);
-       if (data->rsize != 0)
-               server->rsize = nfs_block_size(data->rsize, NULL);
-       if (data->wsize != 0)
-               server->wsize = nfs_block_size(data->wsize, NULL);
-       server->flags = data->flags & NFS_MOUNT_FLAGMASK;
-       server->caps = NFS_CAP_ATOMIC_OPEN;
+       if (!s->s_root) {
+               /* initial superblock/root creation */
+               s->s_flags = flags;
+               nfs_clone_super(s, data->sb);
+       }
 
-       server->acregmin = data->acregmin*HZ;
-       server->acregmax = data->acregmax*HZ;
-       server->acdirmin = data->acdirmin*HZ;
-       server->acdirmax = data->acdirmax*HZ;
+       mntroot = nfs_get_root(s, data->fh);
+       if (IS_ERR(mntroot)) {
+               error = PTR_ERR(mntroot);
+               goto error_splat_super;
+       }
 
-       server->rpc_ops = &nfs_v4_clientops;
+       s->s_flags |= MS_ACTIVE;
+       mnt->mnt_sb = s;
+       mnt->mnt_root = mntroot;
 
-       nfs_init_timeout_values(&timeparms, data->proto, data->timeo, data->retrans);
+       dprintk("<-- nfs_xdev_get_sb() = 0\n");
+       return 0;
 
-       server->retrans_timeo = timeparms.to_initval;
-       server->retrans_count = timeparms.to_retries;
+out_err_nosb:
+       nfs_free_server(server);
+out_err_noserver:
+       dprintk("<-- nfs_xdev_get_sb() = %d [error]\n", error);
+       return error;
 
-       /* Now create transport and client */
-       authflavour = RPC_AUTH_UNIX;
-       if (data->auth_flavourlen != 0) {
-               if (data->auth_flavourlen != 1) {
-                       dprintk("%s: Invalid number of RPC auth flavours %d.\n",
-                                       __FUNCTION__, data->auth_flavourlen);
-                       err = -EINVAL;
-                       goto out_fail;
-               }
-               if (copy_from_user(&authflavour, data->auth_flavours, sizeof(authflavour))) {
-                       err = -EFAULT;
-                       goto out_fail;
-               }
-       }
+error_splat_super:
+       up_write(&s->s_umount);
+       deactivate_super(s);
+       dprintk("<-- nfs_xdev_get_sb() = %d [splat]\n", error);
+       return error;
+}
 
-       server->client = nfs4_create_client(server, &timeparms, data->proto, authflavour);
-       if (IS_ERR(server->client)) {
-               err = PTR_ERR(server->client);
-                       dprintk("%s: cannot create RPC client. Error = %d\n",
-                                       __FUNCTION__, err);
-                       goto out_fail;
-       }
+#ifdef CONFIG_NFS_V4
 
+/*
+ * Finish setting up a cloned NFS4 superblock
+ */
+static void nfs4_clone_super(struct super_block *sb,
+                           const struct super_block *old_sb)
+{
+       sb->s_blocksize_bits = old_sb->s_blocksize_bits;
+       sb->s_blocksize = old_sb->s_blocksize;
+       sb->s_maxbytes = old_sb->s_maxbytes;
        sb->s_time_gran = 1;
-
-       sb->s_op = &nfs4_sops;
-       err = nfs_sb_init(sb, authflavour);
-
- out_fail:
-       return err;
+       sb->s_op = old_sb->s_op;
+       nfs_initialise_sb(sb);
 }
 
-static int nfs4_compare_super(struct super_block *sb, void *data)
+/*
+ * Set up an NFS4 superblock
+ */
+static void nfs4_fill_super(struct super_block *sb)
 {
-       struct nfs_server *server = data;
-       struct nfs_server *old = NFS_SB(sb);
-
-       if (strcmp(server->hostname, old->hostname) != 0)
-               return 0;
-       if (strcmp(server->mnt_path, old->mnt_path) != 0)
-               return 0;
-       return 1;
+       sb->s_time_gran = 1;
+       sb->s_op = &nfs4_sops;
+       nfs_initialise_sb(sb);
 }
 
-static void *
-nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen)
+static void *nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen)
 {
        void *p = NULL;
 
@@ -1297,14 +793,22 @@ nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen)
        return dst;
 }
 
+/*
+ * Get the superblock for an NFS4 mountpoint
+ */
 static int nfs4_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
 {
-       int error;
-       struct nfs_server *server;
-       struct super_block *s;
        struct nfs4_mount_data *data = raw_data;
+       struct super_block *s;
+       struct nfs_server *server;
+       struct sockaddr_in addr;
+       rpc_authflavor_t authflavour;
+       struct nfs_fh mntfh;
+       struct dentry *mntroot;
+       char *mntpath = NULL, *hostname = NULL, ip_addr[16];
        void *p;
+       int error;
 
        if (data == NULL) {
                dprintk("%s: missing data argument\n", __FUNCTION__);
@@ -1315,84 +819,112 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
                return -EINVAL;
        }
 
-       server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
-       if (!server)
-               return -ENOMEM;
-       /* Zero out the NFS state stuff */
-       init_nfsv4_state(server);
-       server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
+       /* We now require that the mount process passes the remote address */
+       if (data->host_addrlen != sizeof(addr))
+               return -EINVAL;
+
+       if (copy_from_user(&addr, data->host_addr, sizeof(addr)))
+               return -EFAULT;
+
+       if (addr.sin_family != AF_INET ||
+           addr.sin_addr.s_addr == INADDR_ANY
+           ) {
+               dprintk("%s: mount program didn't pass remote IP address!\n",
+                               __FUNCTION__);
+               return -EINVAL;
+       }
+       /* RFC3530: The default port for NFS is 2049 */
+       if (addr.sin_port == 0)
+               addr.sin_port = NFS_PORT;
+
+       /* Grab the authentication type */
+       authflavour = RPC_AUTH_UNIX;
+       if (data->auth_flavourlen != 0) {
+               if (data->auth_flavourlen != 1) {
+                       dprintk("%s: Invalid number of RPC auth flavours %d.\n",
+                                       __FUNCTION__, data->auth_flavourlen);
+                       error = -EINVAL;
+                       goto out_err_noserver;
+               }
+
+               if (copy_from_user(&authflavour, data->auth_flavours,
+                                  sizeof(authflavour))) {
+                       error = -EFAULT;
+                       goto out_err_noserver;
+               }
+       }
 
        p = nfs_copy_user_string(NULL, &data->hostname, 256);
        if (IS_ERR(p))
                goto out_err;
-       server->hostname = p;
+       hostname = p;
 
        p = nfs_copy_user_string(NULL, &data->mnt_path, 1024);
        if (IS_ERR(p))
                goto out_err;
-       server->mnt_path = p;
+       mntpath = p;
+
+       dprintk("MNTPATH: %s\n", mntpath);
 
-       p = nfs_copy_user_string(server->ip_addr, &data->client_addr,
-                       sizeof(server->ip_addr) - 1);
+       p = nfs_copy_user_string(ip_addr, &data->client_addr,
+                                sizeof(ip_addr) - 1);
        if (IS_ERR(p))
                goto out_err;
 
-       /* We now require that the mount process passes the remote address */
-       if (data->host_addrlen != sizeof(server->addr)) {
-               error = -EINVAL;
-               goto out_free;
-       }
-       if (copy_from_user(&server->addr, data->host_addr, sizeof(server->addr))) {
-               error = -EFAULT;
-               goto out_free;
-       }
-       if (server->addr.sin_family != AF_INET ||
-           server->addr.sin_addr.s_addr == INADDR_ANY) {
-               dprintk("%s: mount program didn't pass remote IP address!\n",
-                               __FUNCTION__);
-               error = -EINVAL;
-               goto out_free;
-       }
-
-       /* Fire up rpciod if not yet running */
-       error = rpciod_up();
-       if (error < 0) {
-               dprintk("%s: couldn't start rpciod! Error = %d\n",
-                               __FUNCTION__, error);
-               goto out_free;
+       /* Get a volume representation */
+       server = nfs4_create_server(data, hostname, &addr, mntpath, ip_addr,
+                                   authflavour, &mntfh);
+       if (IS_ERR(server)) {
+               error = PTR_ERR(server);
+               goto out_err_noserver;
        }
 
-       s = sget(fs_type, nfs4_compare_super, nfs_set_super, server);
-
+       /* Get a superblock - note that we may end up sharing one that already exists */
+       s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
        if (IS_ERR(s)) {
                error = PTR_ERR(s);
                goto out_free;
        }
 
-       if (s->s_root) {
-               kfree(server->mnt_path);
-               kfree(server->hostname);
-               kfree(server);
-               return simple_set_mnt(mnt, s);
+       if (s->s_fs_info != server) {
+               nfs_free_server(server);
+               server = NULL;
        }
 
-       s->s_flags = flags;
+       if (!s->s_root) {
+               /* initial superblock/root creation */
+               s->s_flags = flags;
+               nfs4_fill_super(s);
+       }
 
-       error = nfs4_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
-       if (error) {
-               up_write(&s->s_umount);
-               deactivate_super(s);
-               return error;
+       mntroot = nfs4_get_root(s, &mntfh);
+       if (IS_ERR(mntroot)) {
+               error = PTR_ERR(mntroot);
+               goto error_splat_super;
        }
+
        s->s_flags |= MS_ACTIVE;
-       return simple_set_mnt(mnt, s);
+       mnt->mnt_sb = s;
+       mnt->mnt_root = mntroot;
+       kfree(mntpath);
+       kfree(hostname);
+       return 0;
+
 out_err:
        error = PTR_ERR(p);
+       goto out_err_noserver;
+
 out_free:
-       kfree(server->mnt_path);
-       kfree(server->hostname);
-       kfree(server);
+       nfs_free_server(server);
+out_err_noserver:
+       kfree(mntpath);
+       kfree(hostname);
        return error;
+
+error_splat_super:
+       up_write(&s->s_umount);
+       deactivate_super(s);
+       goto out_err_noserver;
 }
 
 static void nfs4_kill_super(struct super_block *sb)
@@ -1403,135 +935,140 @@ static void nfs4_kill_super(struct super_block *sb)
        kill_anon_super(sb);
 
        nfs4_renewd_prepare_shutdown(server);
+       nfs_free_server(server);
+}
+
+/*
+ * Clone an NFS4 server record on xdev traversal (FSID-change)
+ */
+static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags,
+                           const char *dev_name, void *raw_data,
+                           struct vfsmount *mnt)
+{
+       struct nfs_clone_mount *data = raw_data;
+       struct super_block *s;
+       struct nfs_server *server;
+       struct dentry *mntroot;
+       int error;
+
+       dprintk("--> nfs4_xdev_get_sb()\n");
+
+       /* create a new volume representation */
+       server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr);
+       if (IS_ERR(server)) {
+               error = PTR_ERR(server);
+               goto out_err_noserver;
+       }
+
+       /* Get a superblock - note that we may end up sharing one that already exists */
+       s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server);
+       if (IS_ERR(s)) {
+               error = PTR_ERR(s);
+               goto out_err_nosb;
+       }
 
-       if (server->client != NULL && !IS_ERR(server->client))
-               rpc_shutdown_client(server->client);
+       if (s->s_fs_info != server) {
+               nfs_free_server(server);
+               server = NULL;
+       }
 
-       destroy_nfsv4_state(server);
+       if (!s->s_root) {
+               /* initial superblock/root creation */
+               s->s_flags = flags;
+               nfs4_clone_super(s, data->sb);
+       }
+
+       mntroot = nfs4_get_root(s, data->fh);
+       if (IS_ERR(mntroot)) {
+               error = PTR_ERR(mntroot);
+               goto error_splat_super;
+       }
 
-       rpciod_down();
+       s->s_flags |= MS_ACTIVE;
+       mnt->mnt_sb = s;
+       mnt->mnt_root = mntroot;
+
+       dprintk("<-- nfs4_xdev_get_sb() = 0\n");
+       return 0;
+
+out_err_nosb:
+       nfs_free_server(server);
+out_err_noserver:
+       dprintk("<-- nfs4_xdev_get_sb() = %d [error]\n", error);
+       return error;
 
-       nfs_free_iostats(server->io_stats);
-       kfree(server->hostname);
-       kfree(server);
-       nfs_release_automount_timer();
+error_splat_super:
+       up_write(&s->s_umount);
+       deactivate_super(s);
+       dprintk("<-- nfs4_xdev_get_sb() = %d [splat]\n", error);
+       return error;
 }
 
 /*
- * Constructs the SERVER-side path
+ * Create an NFS4 server record on referral traversal
  */
-static inline char *nfs4_dup_path(const struct dentry *dentry)
+static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags,
+                               const char *dev_name, void *raw_data,
+                               struct vfsmount *mnt)
 {
-       char *page = (char *) __get_free_page(GFP_USER);
-       char *path;
+       struct nfs_clone_mount *data = raw_data;
+       struct super_block *s;
+       struct nfs_server *server;
+       struct dentry *mntroot;
+       struct nfs_fh mntfh;
+       int error;
 
-       path = nfs4_path(dentry, page, PAGE_SIZE);
-       if (!IS_ERR(path)) {
-               int len = PAGE_SIZE + page - path;
-               char *tmp = path;
+       dprintk("--> nfs4_referral_get_sb()\n");
 
-               path = kmalloc(len, GFP_KERNEL);
-               if (path)
-                       memcpy(path, tmp, len);
-               else
-                       path = ERR_PTR(-ENOMEM);
+       /* create a new volume representation */
+       server = nfs4_create_referral_server(data, &mntfh);
+       if (IS_ERR(server)) {
+               error = PTR_ERR(server);
+               goto out_err_noserver;
        }
-       free_page((unsigned long)page);
-       return path;
-}
 
-static struct super_block *nfs4_clone_sb(struct nfs_server *server, struct nfs_clone_mount *data)
-{
-       const struct dentry *dentry = data->dentry;
-       struct nfs4_client *clp = server->nfs4_state;
-       struct super_block *sb;
-
-       server->fsid = data->fattr->fsid;
-       nfs_copy_fh(&server->fh, data->fh);
-       server->mnt_path = nfs4_dup_path(dentry);
-       if (IS_ERR(server->mnt_path)) {
-               sb = (struct super_block *)server->mnt_path;
-               goto err;
+       /* Get a superblock - note that we may end up sharing one that already exists */
+       s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server);
+       if (IS_ERR(s)) {
+               error = PTR_ERR(s);
+               goto out_err_nosb;
        }
-       sb = sget(&nfs4_fs_type, nfs4_compare_super, nfs_set_super, server);
-       if (IS_ERR(sb) || sb->s_root)
-               goto free_path;
-       nfs4_server_capabilities(server, &server->fh);
-
-       down_write(&clp->cl_sem);
-       atomic_inc(&clp->cl_count);
-       list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
-       up_write(&clp->cl_sem);
-       return sb;
-free_path:
-       kfree(server->mnt_path);
-err:
-       server->mnt_path = NULL;
-       return sb;
-}
 
-static int nfs_clone_nfs4_sb(struct file_system_type *fs_type,
-               int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
-{
-       struct nfs_clone_mount *data = raw_data;
-       return nfs_clone_generic_sb(data, nfs4_clone_sb, nfs_clone_server, mnt);
-}
+       if (s->s_fs_info != server) {
+               nfs_free_server(server);
+               server = NULL;
+       }
 
-static struct super_block *nfs4_referral_sb(struct nfs_server *server, struct nfs_clone_mount *data)
-{
-       struct super_block *sb = ERR_PTR(-ENOMEM);
-       int len;
-
-       len = strlen(data->mnt_path) + 1;
-       server->mnt_path = kmalloc(len, GFP_KERNEL);
-       if (server->mnt_path == NULL)
-               goto err;
-       memcpy(server->mnt_path, data->mnt_path, len);
-       memcpy(&server->addr, data->addr, sizeof(struct sockaddr_in));
-
-       sb = sget(&nfs4_fs_type, nfs4_compare_super, nfs_set_super, server);
-       if (IS_ERR(sb) || sb->s_root)
-               goto free_path;
-       return sb;
-free_path:
-       kfree(server->mnt_path);
-err:
-       server->mnt_path = NULL;
-       return sb;
-}
+       if (!s->s_root) {
+               /* initial superblock/root creation */
+               s->s_flags = flags;
+               nfs4_fill_super(s);
+       }
 
-static struct nfs_server *nfs4_referral_server(struct super_block *sb, struct nfs_clone_mount *data)
-{
-       struct nfs_server *server = NFS_SB(sb);
-       struct rpc_timeout timeparms;
-       int proto, timeo, retrans;
-       void *err;
-
-       proto = IPPROTO_TCP;
-       /* Since we are following a referral and there may be alternatives,
-          set the timeouts and retries to low values */
-       timeo = 2;
-       retrans = 1;
-       nfs_init_timeout_values(&timeparms, proto, timeo, retrans);
-
-       server->client = nfs4_create_client(server, &timeparms, proto, data->authflavor);
-       if (IS_ERR((err = server->client)))
-               goto out_err;
+       mntroot = nfs4_get_root(s, data->fh);
+       if (IS_ERR(mntroot)) {
+               error = PTR_ERR(mntroot);
+               goto error_splat_super;
+       }
 
-       sb->s_time_gran = 1;
-       sb->s_op = &nfs4_sops;
-       err = ERR_PTR(nfs_sb_init(sb, data->authflavor));
-       if (!IS_ERR(err))
-               return server;
-out_err:
-       return (struct nfs_server *)err;
-}
+       s->s_flags |= MS_ACTIVE;
+       mnt->mnt_sb = s;
+       mnt->mnt_root = mntroot;
 
-static int nfs_referral_nfs4_sb(struct file_system_type *fs_type,
-               int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
-{
-       struct nfs_clone_mount *data = raw_data;
-       return nfs_clone_generic_sb(data, nfs4_referral_sb, nfs4_referral_server, mnt);
+       dprintk("<-- nfs4_referral_get_sb() = 0\n");
+       return 0;
+
+out_err_nosb:
+       nfs_free_server(server);
+out_err_noserver:
+       dprintk("<-- nfs4_referral_get_sb() = %d [error]\n", error);
+       return error;
+
+error_splat_super:
+       up_write(&s->s_umount);
+       deactivate_super(s);
+       dprintk("<-- nfs4_referral_get_sb() = %d [splat]\n", error);
+       return error;
 }
 
-#endif
+#endif /* CONFIG_NFS_V4 */
index 7084ac9..c12effb 100644 (file)
@@ -396,6 +396,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
 out:
        clear_bit(BDI_write_congested, &bdi->state);
        wake_up_all(&nfs_write_congestion);
+       writeback_congestion_end();
        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",
                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);
        if (status != 0)
                return status;
@@ -1273,7 +1280,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
                if (time_before(complain, jiffies)) {
                        dprintk("NFS: faulty NFS server %s:"
                                " (committed = %d) != (stable = %d)\n",
-                               NFS_SERVER(data->inode)->hostname,
+                               NFS_SERVER(data->inode)->nfs_client->cl_hostname,
                                resp->verf->committed, argp->stable);
                        complain = jiffies + 300 * HZ;
                }
index 54b37b1..8583d99 100644 (file)
@@ -375,16 +375,28 @@ nfsd4_probe_callback(struct nfs4_client *clp)
 {
        struct sockaddr_in      addr;
        struct nfs4_callback    *cb = &clp->cl_callback;
-       struct rpc_timeout      timeparms;
-       struct rpc_xprt *       xprt;
+       struct rpc_timeout      timeparms = {
+               .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_stat *       stat = &cb->cb_stat;
-       struct rpc_clnt *       clnt;
+       struct rpc_create_args args = {
+               .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 = {
                .rpc_proc       = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
                .rpc_argp       = clp,
        };
-       char                    hostname[32];
        int status;
 
        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_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 */
        program->name = "nfs4_cb";
        program->number = cb->cb_prog;
        program->nrvers = ARRAY_SIZE(nfs_cb_version);
        program->version = nfs_cb_version;
-       program->stats = stat;
+       program->stats = &cb->cb_stat;
 
        /* Initialize rpc_stat */
-       memset(stat, 0, sizeof(struct rpc_stat));
-       stat->program = program;
-
-       /* Create RPC client
-        *
-        * XXX AUTH_UNIX only - need AUTH_GSS....
-        */
-       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)) {
+       memset(program->stats, 0, sizeof(cb->cb_stat));
+       program->stats->program = program;
+
+       /* Create RPC client */
+       cb->cb_client = rpc_create(&args);
+       if (!cb->cb_client) {
                dprintk("NFSD: couldn't create callback client\n");
                goto out_err;
        }
-       clnt->cl_intr = 0;
-       clnt->cl_softrtry = 1;
 
        /* Kick rpciod, put the call on the wire. */
-
-       if (rpciod_up() != 0) {
-               dprintk("nfsd: couldn't start rpciod for callbacks!\n");
+       if (rpciod_up() != 0)
                goto out_clnt;
-       }
-
-       cb->cb_client = clnt;
 
        /* the task holds a reference to the nfs4_client struct */
        atomic_inc(&clp->cl_count);
@@ -448,7 +436,7 @@ nfsd4_probe_callback(struct nfs4_client *clp)
        msg.rpc_cred = nfsd4_lookupcred(clp,0);
        if (IS_ERR(msg.rpc_cred))
                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);
 
        if (status != 0) {
@@ -462,7 +450,7 @@ out_rpciod:
        rpciod_down();
        cb->cb_client = NULL;
 out_clnt:
-       rpc_shutdown_client(clnt);
+       rpc_shutdown_client(cb->cb_client);
 out_err:
        dprintk("NFSD: warning: no callback path to client %.*s\n",
                (int)clp->cl_name.len, clp->cl_name.data);
index aafe827..96c9040 100644 (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 void blk_queue_invalidate_tags(request_queue_t *);
 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 int blkdev_issue_flush(struct block_device *, sector_t *);
index 471781f..44605be 100644 (file)
@@ -221,6 +221,7 @@ static inline int dname_external(struct dentry *dentry)
  */
 extern void d_instantiate(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 *);
 
 /* allocate/de-allocate */
index 6c2066c..3b5b041 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/in.h>
 #include <linux/mm.h>
 #include <linux/pagemap.h>
+#include <linux/rbtree.h>
 #include <linux/rwsem.h>
 #include <linux/wait.h>
 
@@ -69,6 +70,8 @@
  * NFSv3/v4 Access mode cache entry
  */
 struct nfs_access_entry {
+       struct rb_node          rb_node;
+       struct list_head        lru;
        unsigned long           jiffies;
        struct rpc_cred *       cred;
        int                     mask;
@@ -145,7 +148,9 @@ struct nfs_inode {
         */
        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
        struct posix_acl        *acl_access;
        struct posix_acl        *acl_default;
@@ -199,6 +204,7 @@ struct nfs_inode {
 #define NFS_INO_REVALIDATING   (0)             /* revalidating attrs */
 #define NFS_INO_ADVISE_RDPLUS  (1)             /* advise readdirplus */
 #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)
 {
@@ -209,8 +215,7 @@ static inline struct nfs_inode *NFS_I(struct inode *inode)
 #define NFS_FH(inode)                  (&NFS_I(inode)->fh)
 #define NFS_SERVER(inode)              (NFS_SB(inode->i_sb))
 #define NFS_CLIENT(inode)              (NFS_SERVER(inode)->client)
-#define NFS_PROTO(inode)               (NFS_SERVER(inode)->rpc_ops)
-#define NFS_ADDR(inode)                        (RPC_PEERADDR(NFS_CLIENT(inode)))
+#define NFS_PROTO(inode)               (NFS_SERVER(inode)->nfs_client->rpc_ops)
 #define NFS_COOKIEVERF(inode)          (NFS_I(inode)->cookieverf)
 #define NFS_READTIME(inode)            (NFS_I(inode)->read_cache_jiffies)
 #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_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_zap_cache(struct inode *inode);
 extern int nfs_open(struct inode *, struct file *);
 extern int nfs_release(struct inode *, struct file *);
 extern int nfs_attribute_timeout(struct inode *inode);
@@ -579,6 +585,7 @@ extern void * nfs_root_data(void);
 #define NFSDBG_FILE            0x0040
 #define NFSDBG_ROOT            0x0080
 #define NFSDBG_CALLBACK                0x0100
+#define NFSDBG_CLIENT          0x0200
 #define NFSDBG_ALL             0xFFFF
 
 #ifdef __KERNEL__
index 6b4a13c..7ccfc7e 100644 (file)
@@ -6,14 +6,80 @@
 
 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.
  */
 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_sys;     /* 2nd handle for FSINFO */
        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 backing_dev_info backing_dev_info;
        int                     flags;          /* various flags */
@@ -29,24 +95,14 @@ struct nfs_server {
        unsigned int            acregmax;
        unsigned int            acdirmin;
        unsigned int            acdirmax;
-       unsigned long           retrans_timeo;  /* retransmit timeout */
-       unsigned int            retrans_count;  /* number of retransmit tries */
        unsigned int            namelen;
-       char *                  hostname;       /* remote hostname */
-       struct nfs_fh           fh;
-       struct sockaddr_in      addr;
+
        struct nfs_fsid         fsid;
+       __u64                   maxfilesize;    /* maximum file size */
        unsigned long           mount_time;     /* when this fs was mounted */
+       dev_t                   s_dev;          /* superblock dev numbers */
+
 #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
                                                   of attributes supported on this
                                                   filesystem */
@@ -54,6 +110,7 @@ struct nfs_server {
                                                   that are supported on this
                                                   filesystem */
 #endif
+       void (*destroy)(struct nfs_server *);
 };
 
 /* Server capabilities */
index 102e560..15a9f3b 100644 (file)
@@ -62,15 +62,15 @@ struct idmap_msg {
 #ifdef __KERNEL__
 
 /* Forward declaration to make this header independent of others */
-struct nfs4_client;
+struct nfs_client;
 
-void nfs_idmap_new(struct nfs4_client *);
-void nfs_idmap_delete(struct nfs4_client *);
+int nfs_idmap_new(struct nfs_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_group_to_gid(struct nfs4_client *, const char *, size_t, __u32 *);
-int nfs_map_uid_to_name(struct nfs4_client *, __u32, char *);
-int nfs_map_gid_to_group(struct nfs4_client *, __u32, char *);
+int nfs_map_name_to_uid(struct nfs_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 nfs_client *, __u32, char *);
+int nfs_map_gid_to_group(struct nfs_client *, __u32, char *);
 
 extern unsigned int nfs_idmap_cache_timeout;
 #endif /* __KERNEL__ */
index 41e5a19..dc5397d 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef _LINUX_NFS_XDR_H
 #define _LINUX_NFS_XDR_H
 
-#include <linux/sunrpc/xprt.h>
 #include <linux/nfsacl.h>
 
 /*
@@ -359,8 +358,8 @@ struct nfs_symlinkargs {
        struct nfs_fh *         fromfh;
        const char *            fromname;
        unsigned int            fromlen;
-       const char *            topath;
-       unsigned int            tolen;
+       struct page **          pages;
+       unsigned int            pathlen;
        struct iattr *          sattr;
 };
 
@@ -435,8 +434,8 @@ struct nfs3_symlinkargs {
        struct nfs_fh *         fromfh;
        const char *            fromname;
        unsigned int            fromlen;
-       const char *            topath;
-       unsigned int            tolen;
+       struct page **          pages;
+       unsigned int            pathlen;
        struct iattr *          sattr;
 };
 
@@ -534,7 +533,10 @@ struct nfs4_accessres {
 struct nfs4_create_arg {
        u32                             ftype;
        union {
-               struct qstr *           symlink;    /* NF4LNK */
+               struct {
+                       struct page **  pages;
+                       unsigned int    len;
+               } symlink;   /* NF4LNK */
                struct {
                        u32             specdata1;
                        u32             specdata2;
@@ -770,6 +772,9 @@ struct nfs_rpc_ops {
 
        int     (*getroot) (struct nfs_server *, struct nfs_fh *,
                            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 *,
                            struct nfs_fattr *);
        int     (*setattr) (struct dentry *, struct nfs_fattr *,
@@ -791,9 +796,8 @@ struct nfs_rpc_ops {
        int     (*rename)  (struct inode *, struct qstr *,
                            struct inode *, struct qstr *);
        int     (*link)    (struct inode *, struct inode *, struct qstr *);
-       int     (*symlink) (struct inode *, struct qstr *, struct qstr *,
-                           struct iattr *, struct nfs_fh *,
-                           struct nfs_fattr *);
+       int     (*symlink) (struct inode *, struct dentry *, struct page *,
+                           unsigned int, struct iattr *);
        int     (*mkdir)   (struct inode *, struct dentry *, struct iattr *);
        int     (*rmdir)   (struct inode *, struct qstr *);
        int     (*readdir) (struct dentry *, struct rpc_cred *,
@@ -806,6 +810,7 @@ struct nfs_rpc_ops {
                            struct nfs_fsinfo *);
        int     (*pathconf) (struct nfs_server *, struct nfs_fh *,
                             struct nfs_pathconf *);
+       int     (*set_capabilities)(struct nfs_server *, struct nfs_fh *);
        u32 *   (*decode_dirent)(u32 *, struct nfs_entry *, int plus);
        void    (*read_setup)   (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
  */
-extern struct nfs_rpc_ops      nfs_v2_clientops;
-extern struct nfs_rpc_ops      nfs_v3_clientops;
-extern struct nfs_rpc_ops      nfs_v4_clientops;
+extern const struct nfs_rpc_ops        nfs_v2_clientops;
+extern const struct nfs_rpc_ops        nfs_v3_clientops;
+extern const struct nfs_rpc_ops        nfs_v4_clientops;
 extern struct rpc_version      nfs_version2;
 extern struct rpc_version      nfs_version3;
 extern struct rpc_version      nfs_version4;
index 8fe9f35..f6d1d64 100644 (file)
 #include <linux/sunrpc/timer.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;
 
 /*
@@ -40,7 +28,9 @@ struct rpc_clnt {
        atomic_t                cl_users;       /* number of references */
        struct rpc_xprt *       cl_xprt;        /* transport */
        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_protname;    /* protocol name */
@@ -55,7 +45,6 @@ struct rpc_clnt {
                                cl_dead     : 1;/* abandoned */
 
        struct rpc_rtt *        cl_rtt;         /* RTO estimator data */
-       struct rpc_portmap *    cl_pmap;        /* port mapping */
 
        int                     cl_nodelen;     /* nodename length */
        char                    cl_nodename[UNX_MAXNODENAME];
@@ -64,14 +53,8 @@ struct rpc_clnt {
        struct dentry *         cl_dentry;      /* inode */
        struct rpc_clnt *       cl_parent;      /* Points to parent of clones */
        struct rpc_rtt          cl_rtt_default;
-       struct rpc_portmap      cl_pmap_default;
        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
@@ -106,24 +89,36 @@ struct rpc_procinfo {
        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__
 
-struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname,
-                               struct rpc_program *info,
-                               u32 version, rpc_authflavor_t authflavor);
-struct rpc_clnt *rpc_new_client(struct rpc_xprt *xprt, char *servname,
-                               struct rpc_program *info,
-                               u32 version, rpc_authflavor_t authflavor);
+struct rpc_create_args {
+       int                     protocol;
+       struct sockaddr         *address;
+       size_t                  addrsize;
+       struct rpc_timeout      *timeout;
+       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_program *, int);
 struct rpc_clnt *rpc_clone_client(struct rpc_clnt *);
 int            rpc_shutdown_client(struct rpc_clnt *);
 int            rpc_destroy_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 *);
 
 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 *);
 void           rpc_force_rebind(struct rpc_clnt *);
 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
index a481472..a2eb9b4 100644 (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 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 struct vfsmount *rpc_get_mount(void);
 extern void rpc_put_mount(void);
index 82a91bb..f399c13 100644 (file)
@@ -127,7 +127,6 @@ struct rpc_call_ops {
  */
 #define RPC_TASK_ASYNC         0x0001          /* is an async task */
 #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_TASK_ROOTCREDS     0x0040          /* force root creds */
 #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_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_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS)
 #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);
 struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags,
                                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,
                                int flags, const struct rpc_call_ops *ops,
                                void *data);
@@ -261,8 +258,6 @@ void                rpc_release_task(struct rpc_task *);
 void           rpc_exit_task(struct rpc_task *);
 void           rpc_killall_tasks(struct rpc_clnt *);
 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_wait_queue(struct rpc_wait_queue *, const char *);
 void           rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *,
index 3a0cca2..bdeba85 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/uio.h>
 #include <linux/socket.h>
 #include <linux/in.h>
+#include <linux/kref.h>
 #include <linux/sunrpc/sched.h>
 #include <linux/sunrpc/xdr.h>
 
@@ -51,6 +52,14 @@ struct rpc_timeout {
        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_xprt;
 struct seq_file;
@@ -103,8 +112,10 @@ struct rpc_rqst {
 
 struct rpc_xprt_ops {
        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);
        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            (*connect)(struct rpc_task *task);
        void *          (*buf_alloc)(struct rpc_task *task, size_t size);
@@ -119,12 +130,14 @@ struct rpc_xprt_ops {
 };
 
 struct rpc_xprt {
+       struct kref             kref;           /* Reference count */
        struct rpc_xprt_ops *   ops;            /* transport methods */
        struct socket *         sock;           /* BSD socket layer */
        struct sock *           inet;           /* INET layer */
 
        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 */
 
        unsigned long           cong;           /* current congestion */
@@ -138,6 +151,7 @@ struct rpc_xprt {
        unsigned int            tsh_size;       /* size of transport specific
                                                   header */
 
+       struct rpc_wait_queue   binding;        /* requests waiting on rpcbind */
        struct rpc_wait_queue   sending;        /* requests waiting to send */
        struct rpc_wait_queue   resend;         /* requests waiting to resend */
        struct rpc_wait_queue   pending;        /* requests in flight */
@@ -205,6 +219,8 @@ struct rpc_xprt {
        void                    (*old_data_ready)(struct sock *, int);
        void                    (*old_state_change)(struct sock *);
        void                    (*old_write_space)(struct sock *);
+
+       char *                  address_strings[RPC_DISPLAY_MAX];
 };
 
 #define XPRT_LAST_FRAG         (1 << 0)
@@ -217,12 +233,12 @@ struct rpc_xprt {
 /*
  * 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);
 
 /*
  * 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_reserve(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_cong(struct rpc_xprt *xprt, 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)
 {
@@ -269,6 +286,8 @@ int                 xs_setup_tcp(struct rpc_xprt *xprt, struct rpc_timeout *to);
 #define XPRT_CONNECTED         (1)
 #define XPRT_CONNECTING                (2)
 #define XPRT_CLOSE_WAIT                (3)
+#define XPRT_BOUND             (4)
+#define XPRT_BINDING           (5)
 
 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);
 }
 
+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 /* _LINUX_SUNRPC_XPRT_H */
index 9e38b56..0422036 100644 (file)
@@ -85,6 +85,7 @@ int wakeup_pdflush(long nr_pages);
 void laptop_io_completion(void);
 void laptop_sync_completion(void);
 void throttle_vm_writeout(void);
+void writeback_congestion_end(void);
 
 /* These are exported to sysctl. */
 extern int dirty_background_ratio;
index e630188..77a0bc4 100644 (file)
@@ -802,6 +802,15 @@ int test_set_page_writeback(struct page *page)
 }
 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
  * passed tag.
index ef1cf5b..6eed3e1 100644 (file)
@@ -88,7 +88,6 @@ struct gss_auth {
        struct list_head upcalls;
        struct rpc_clnt *client;
        struct dentry *dentry;
-       char path[48];
        spinlock_t lock;
 };
 
@@ -690,10 +689,8 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
        if (err)
                goto err_put_mech;
 
-       snprintf(gss_auth->path, sizeof(gss_auth->path), "%s/%s",
-                       clnt->cl_pathname,
-                       gss_auth->mech->gm_name);
-       gss_auth->dentry = rpc_mkpipe(gss_auth->path, clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
+       gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name,
+                       clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
        if (IS_ERR(gss_auth->dentry)) {
                err = PTR_ERR(gss_auth->dentry);
                goto err_put_mech;
index 3e19d32..084a0ad 100644 (file)
@@ -97,17 +97,7 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
        }
 }
 
-/*
- * 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)
+static 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_clnt         *clnt = NULL;
@@ -147,16 +137,12 @@ rpc_new_client(struct rpc_xprt *xprt, char *servname,
        clnt->cl_procinfo = version->procs;
        clnt->cl_maxproc  = version->nrprocs;
        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_vers     = version->number;
-       clnt->cl_prot     = xprt->prot;
        clnt->cl_stats    = program->stats;
        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_rtt = &clnt->cl_rtt_default;
@@ -191,40 +177,71 @@ out_no_path:
                kfree(clnt->cl_server);
        kfree(clnt);
 out_err:
-       xprt_destroy(xprt);
+       xprt_put(xprt);
 out_no_xprt:
        return ERR_PTR(err);
 }
 
-/**
- * Create an RPC client
- * @xprt - pointer to xprt struct
- * @servname - name of server
- * @info - rpc_program
- * @version - rpc_program version
- * @authflavor - rpc_auth flavour to use
+/*
+ * rpc_create - create an RPC client and transport with one call
+ * @args: rpc_clnt create argument structure
  *
- * Creates an RPC client structure, then pings the server in order to
- * determine if it is up, and if it supports this program and version.
+ * Creates and initializes an RPC transport and an RPC client.
  *
- * This function should never be called by asynchronous tasks such as
- * the portmapper.
+ * It can ping the server in order to determine if it is up, and to see if
+ * 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_program *info, u32 version, rpc_authflavor_t authflavor)
+struct rpc_clnt *rpc_create(struct rpc_create_args *args)
 {
+       struct rpc_xprt *xprt;
        struct rpc_clnt *clnt;
-       int err;
-       
-       clnt = rpc_new_client(xprt, servname, info, version, authflavor);
+
+       xprt = xprt_create_transport(args->protocol, args->address,
+                                       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))
                return clnt;
-       err = rpc_ping(clnt, RPC_TASK_SOFT|RPC_TASK_NOINTR);
-       if (err == 0)
-               return clnt;
-       rpc_shutdown_client(clnt);
-       return ERR_PTR(err);
+
+       if (!(args->flags & RPC_CLNT_CREATE_NOPING)) {
+               int err = rpc_ping(clnt, RPC_TASK_SOFT|RPC_TASK_NOINTR);
+               if (err != 0) {
+                       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
@@ -244,8 +261,7 @@ rpc_clone_client(struct rpc_clnt *clnt)
        atomic_set(&new->cl_users, 0);
        new->cl_parent = clnt;
        atomic_inc(&clnt->cl_count);
-       /* Duplicate portmapper */
-       rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait");
+       new->cl_xprt = xprt_get(clnt->cl_xprt);
        /* Turn off autobind on clones */
        new->cl_autobind = 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);
        if (new->cl_auth)
                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;
 out_no_clnt:
        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_put_mount();
        }
-       if (clnt->cl_xprt) {
-               xprt_destroy(clnt->cl_xprt);
-               clnt->cl_xprt = NULL;
-       }
        if (clnt->cl_server != clnt->cl_inline_name)
                kfree(clnt->cl_server);
 out_free:
        rpc_free_iostats(clnt->cl_metrics);
        clnt->cl_metrics = NULL;
+       xprt_put(clnt->cl_xprt);
        kfree(clnt);
        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;
 }
 
+/**
+ * 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
 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;
 }
-EXPORT_SYMBOL(rpc_max_payload);
+EXPORT_SYMBOL_GPL(rpc_max_payload);
 
 /**
  * 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)
 {
        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
@@ -781,16 +827,16 @@ call_encode(struct rpc_task *task)
 static void
 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",
                                task->tk_pid, task->tk_status);
 
        task->tk_action = call_connect;
-       if (!clnt->cl_port) {
+       if (!xprt_bound(xprt)) {
                task->tk_action = call_bind_status;
-               task->tk_timeout = task->tk_xprt->bind_timeout;
-               rpc_getport(task, clnt);
+               task->tk_timeout = xprt->bind_timeout;
+               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",
                                task->tk_pid);
                rpc_delay(task, 3*HZ);
-               goto retry_bind;
+               goto retry_timeout;
        case -ETIMEDOUT:
                dprintk("RPC: %4d rpcbind request timed out\n",
                                task->tk_pid);
-               if (RPC_IS_SOFT(task)) {
-                       status = -EIO;
-                       break;
-               }
-               goto retry_bind;
+               goto retry_timeout;
        case -EPFNOSUPPORT:
                dprintk("RPC: %4d remote rpcbind service unavailable\n",
                                task->tk_pid);
@@ -836,16 +878,13 @@ call_bind_status(struct rpc_task *task)
                dprintk("RPC: %4d unrecognized rpcbind error (%d)\n",
                                task->tk_pid, -task->tk_status);
                status = -EIO;
-               break;
        }
 
        rpc_exit(task, status);
        return;
 
-retry_bind:
-       task->tk_status = 0;
-       task->tk_action = call_bind;
-       return;
+retry_timeout:
+       task->tk_action = call_timeout;
 }
 
 /*
@@ -893,14 +932,16 @@ call_connect_status(struct rpc_task *task)
 
        switch (status) {
        case -ENOTCONN:
-       case -ETIMEDOUT:
        case -EAGAIN:
                task->tk_action = call_bind;
-               break;
-       default:
-               rpc_exit(task, -EIO);
-               break;
+               if (!RPC_IS_SOFT(task))
+                       return;
+               /* if soft mounted, test if we've timed out */
+       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;
        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:
                task->tk_action = call_timeout;
                break;
@@ -1001,7 +1050,6 @@ call_status(struct rpc_task *task)
                printk("%s: RPC call returned error %d\n",
                               clnt->cl_protname, -status);
                rpc_exit(task, status);
-               break;
        }
 }
 
@@ -1069,10 +1117,10 @@ call_decode(struct rpc_task *task)
                        clnt->cl_stats->rpcretrans++;
                        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);
-               rpc_exit(task, -EIO);
-               return;
+               task->tk_action = call_timeout;
+               goto out_retry;
        }
 
        /*
index 623180f..c04609d 100644 (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>
  */
@@ -13,7 +15,6 @@
 #include <linux/uio.h>
 #include <linux/in.h>
 #include <linux/sunrpc/clnt.h>
-#include <linux/sunrpc/xprt.h>
 #include <linux/sunrpc/sched.h>
 
 #ifdef RPC_DEBUG
 #define PMAP_UNSET             2
 #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_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 DEFINE_SPINLOCK(pmap_lock);
 
-/*
- * 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)
+static void pmap_getport_prepare(struct rpc_task *task, void *calldata)
 {
-       struct rpc_portmap *map = clnt->cl_pmap;
-       struct sockaddr_in *sap = &clnt->cl_xprt->addr;
+       struct portmap_args *map = calldata;
        struct rpc_message msg = {
                .rpc_proc       = &pmap_procedures[PMAP_GETPORT],
                .rpc_argp       = map,
-               .rpc_resp       = &clnt->cl_port,
-               .rpc_cred       = NULL
+               .rpc_resp       = &map->pm_port,
        };
+
+       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;
+       struct rpc_task *child;
+       int status;
 
-       dprintk("RPC: %4d rpc_getport(%s, %d, %d, %d)\n",
+       dprintk("RPC: %4d rpc_getport(%s, %u, %u, %d)\n",
                        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 */
        BUG_ON(clnt->cl_parent != clnt);
 
-       spin_lock(&pmap_lock);
-       if (map->pm_binding) {
-               rpc_sleep_on(&map->pm_bindwait, task, NULL, NULL);
-               spin_unlock(&pmap_lock);
+       if (xprt_test_and_set_binding(xprt)) {
+               task->tk_status = -EACCES;      /* tell caller to check again */
+               rpc_sleep_on(&xprt->binding, task, NULL, NULL);
                return;
        }
-       map->pm_binding = 1;
-       spin_unlock(&pmap_lock);
 
-       pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot, 0);
-       if (IS_ERR(pmap_clnt)) {
-               task->tk_status = PTR_ERR(pmap_clnt);
+       /* Someone else may have bound if we slept */
+       status = 0;
+       if (xprt_bound(xprt))
+               goto bailout_nofree;
+
+       status = -ENOMEM;
+       map = pmap_map_alloc();
+       if (!map)
+               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;
-       }
-       task->tk_status = 0;
 
-       /*
-        * Note: rpc_new_child will release client after a failure.
-        */
-       if (!(child = rpc_new_child(pmap_clnt, task)))
+       status = -EIO;
+       child = rpc_run_task(pmap_clnt, RPC_TASK_ASYNC, &pmap_getport_ops, map);
+       if (IS_ERR(child))
                goto bailout;
+       rpc_release_task(child);
 
-       /* Setup the call info struct */
-       rpc_call_setup(child, &msg, 0);
+       rpc_sleep_on(&xprt->binding, task, NULL, NULL);
 
-       /* ... and run the child task */
        task->tk_xprt->stat.bind_count++;
-       rpc_run_child(task, child, pmap_getport_done);
        return;
 
 bailout:
-       spin_lock(&pmap_lock);
-       map->pm_binding = 0;
-       rpc_wake_up(&map->pm_bindwait);
-       spin_unlock(&pmap_lock);
-       rpc_exit(task, -EIO);
+       pmap_map_free(map);
+       xprt_put(xprt);
+bailout_nofree:
+       task->tk_status = status;
+       pmap_wake_portmap_waiters(xprt, status);
 }
 
 #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_vers        = vers,
                .pm_prot        = prot,
@@ -112,7 +174,7 @@ rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot)
        char            hostname[32];
        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);
 
        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
 
-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 rpc_xprt *xprt = task->tk_xprt;
-       struct rpc_portmap *map = clnt->cl_pmap;
-
-       dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n",
-                       task->tk_pid, task->tk_status, clnt->cl_port);
-
-       xprt->ops->set_port(xprt, 0);
-       if (task->tk_status < 0) {
-               /* Make the calling task exit with an error */
-               task->tk_action = rpc_exit_task;
-       } else if (clnt->cl_port == 0) {
-               /* Program not registered */
-               rpc_exit(task, -EACCES);
+       struct portmap_args *map = data;
+       struct rpc_xprt *xprt = map->pm_xprt;
+       int status = child->tk_status;
+
+       if (status < 0) {
+               /* Portmapper not available */
+               xprt->ops->set_port(xprt, 0);
+       } else if (map->pm_port == 0) {
+               /* Requested RPC service wasn't registered */
+               xprt->ops->set_port(xprt, 0);
+               status = -EACCES;
        } else {
-               xprt->ops->set_port(xprt, clnt->cl_port);
-               clnt->cl_port = htons(clnt->cl_port);
+               /* Succeeded */
+               xprt->ops->set_port(xprt, map->pm_port);
+               xprt_set_bound(xprt);
+               status = 0;
        }
-       spin_lock(&pmap_lock);
-       map->pm_binding = 0;
-       rpc_wake_up(&map->pm_bindwait);
-       spin_unlock(&pmap_lock);
+
+       dprintk("RPC: %4d pmap_getport_done(status %d, port %u)\n",
+                       child->tk_pid, status, map->pm_port);
+
+       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.
  */
-int
-rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
+int rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
 {
        struct sockaddr_in      sin = {
                .sin_family     = AF_INET,
                .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
        };
-       struct rpc_portmap      map = {
+       struct portmap_args     map = {
                .pm_prog        = prog,
                .pm_vers        = vers,
                .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;
        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);
 
        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;
 }
 
-static struct rpc_clnt *
-pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto, int privileged)
+static struct rpc_clnt *pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto, int privileged)
 {
-       struct rpc_xprt *xprt;
-       struct rpc_clnt *clnt;
-
-       /* printk("pmap: create xprt\n"); */
-       xprt = xprt_create_proto(proto, srvaddr, NULL);
-       if (IS_ERR(xprt))
-               return (struct rpc_clnt *)xprt;
-       xprt->ops->set_port(xprt, RPC_PMAP_PORT);
+       struct rpc_create_args args = {
+               .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),
+       };
+
+       srvaddr->sin_port = htons(RPC_PMAP_PORT);
        if (!privileged)
-               xprt->resvport = 0;
-
-       /* 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;
+               args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
+       return rpc_create(&args);
 }
 
 /*
  * XDR encode/decode functions for PMAP
  */
-static int
-xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct rpc_portmap *map)
+static int xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct portmap_args *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);
        *p++ = htonl(map->pm_prog);
        *p++ = htonl(map->pm_vers);
@@ -249,15 +313,13 @@ xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct rpc_portmap *map)
        return 0;
 }
 
-static int
-xdr_decode_port(struct rpc_rqst *req, u32 *p, unsigned short *portp)
+static int xdr_decode_port(struct rpc_rqst *req, u32 *p, unsigned short *portp)
 {
        *portp = (unsigned short) ntohl(*p++);
        return 0;
 }
 
-static int
-xdr_decode_bool(struct rpc_rqst *req, u32 *p, unsigned int *boolp)
+static int xdr_decode_bool(struct rpc_rqst *req, u32 *p, unsigned int *boolp)
 {
        *boolp = (unsigned int) ntohl(*p++);
        return 0;
index 0b1a1ac..dfa504f 100644 (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, "service: %s (%d) version %d\n", clnt->cl_protname,
                        clnt->cl_prog, clnt->cl_vers);
-       seq_printf(m, "address: %u.%u.%u.%u\n",
-                       NIPQUAD(clnt->cl_xprt->addr.sin_addr.s_addr));
-       seq_printf(m, "protocol: %s\n",
-                       clnt->cl_xprt->prot == IPPROTO_UDP ? "udp" : "tcp");
+       seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR));
+       seq_printf(m, "protocol: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PROTO));
        return 0;
 }
 
@@ -623,17 +621,13 @@ __rpc_rmdir(struct inode *dir, struct dentry *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 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);
-       dentry = lookup_one_len(nd->last.name, nd->dentry, nd->last.len);
+       dentry = lookup_one_len(name, parent, len);
        if (IS_ERR(dentry))
                goto out_err;
        if (dentry->d_inode) {
@@ -644,7 +638,20 @@ rpc_lookup_negative(char *path, struct nameidata *nd)
        return dentry;
 out_err:
        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;
 }
 
@@ -703,18 +710,17 @@ rpc_rmdir(struct dentry *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 inode *dir, *inode;
        struct rpc_inode *rpci;
 
-       dentry = rpc_lookup_negative(path, &nd);
+       dentry = rpc_lookup_create(parent, name, strlen(name));
        if (IS_ERR(dentry))
                return dentry;
-       dir = nd.dentry->d_inode;
-       inode = rpc_get_inode(dir->i_sb, S_IFSOCK | S_IRUSR | S_IWUSR);
+       dir = parent->d_inode;
+       inode = rpc_get_inode(dir->i_sb, S_IFIFO | S_IRUSR | S_IWUSR);
        if (!inode)
                goto err_dput;
        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);
 out:
        mutex_unlock(&dir->i_mutex);
-       rpc_release_path(&nd);
        return dentry;
 err_dput:
        dput(dentry);
        dentry = ERR_PTR(-ENOMEM);
-       printk(KERN_WARNING "%s: %s() failed to create pipe %s (errno = %d)\n",
-                       __FILE__, __FUNCTION__, path, -ENOMEM);
+       printk(KERN_WARNING "%s: %s() failed to create pipe %s/%s (errno = %d)\n",
+                       __FILE__, __FUNCTION__, parent->d_name.name, name,
+                       -ENOMEM);
        goto out;
 }
 
index 5c3eee7..6390461 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/mutex.h>
 
 #include <linux/sunrpc/clnt.h>
-#include <linux/sunrpc/xprt.h>
 
 #ifdef RPC_DEBUG
 #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                    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.
  */
@@ -323,16 +316,6 @@ static void rpc_make_runnable(struct rpc_task *task)
                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.
  * 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);
 }
 
+static void __rpc_atrun(struct rpc_task *task)
+{
+       rpc_wake_up_task(task);
+}
+
 /*
  * 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;
        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
  */
@@ -933,72 +912,6 @@ struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags,
 }
 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.
  * XXX: kill their descendants as well?
index f38f939..26c0531 100644 (file)
@@ -36,8 +36,6 @@ EXPORT_SYMBOL(rpc_wake_up_status);
 EXPORT_SYMBOL(rpc_release_task);
 
 /* RPC client functions */
-EXPORT_SYMBOL(rpc_create_client);
-EXPORT_SYMBOL(rpc_new_client);
 EXPORT_SYMBOL(rpc_clone_client);
 EXPORT_SYMBOL(rpc_bind_new_program);
 EXPORT_SYMBOL(rpc_destroy_client);
@@ -57,7 +55,6 @@ EXPORT_SYMBOL(rpc_queue_upcall);
 EXPORT_SYMBOL(rpc_mkpipe);
 
 /* Client transport */
-EXPORT_SYMBOL(xprt_create_proto);
 EXPORT_SYMBOL(xprt_set_timeout);
 
 /* Client credential cache */
index bcbdf64..8142fdb 100644 (file)
@@ -19,8 +19,6 @@
 #include <linux/unistd.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_INIT (HZ/5)
index e8c2bc4..1f786f6 100644 (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,
                        xprt, (xprt_connected(xprt) ? "is" : "is not"));
 
-       if (!xprt->addr.sin_port) {
+       if (!xprt_bound(xprt)) {
                task->tk_status = -EIO;
                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);
                xprt_release_write(xprt, task);
                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_xid     = xprt_alloc_xid(xprt);
        req->rq_release_snd_buf = NULL;
+       xprt_reset_majortimeo(req);
        dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid,
                        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;
 }
 
-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;
        struct rpc_xprt *xprt;
        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);
-
-       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) {
        case IPPROTO_UDP:
@@ -908,14 +918,15 @@ static struct rpc_xprt *xprt_setup(int proto, struct sockaddr_in *ap, struct rpc
        default:
                printk(KERN_ERR "RPC: unrecognized transport protocol: %d\n",
                                proto);
-               result = -EIO;
-               break;
+               return ERR_PTR(-EIO);
        }
        if (result) {
                kfree(xprt);
+               dprintk("RPC:      xprt_create_transport: failed, %d\n", result);
                return ERR_PTR(result);
        }
 
+       kref_init(&xprt->kref);
        spin_lock_init(&xprt->transport_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->cwnd = RPC_INITCWND;
 
+       rpc_init_wait_queue(&xprt->binding, "xprt_binding");
        rpc_init_wait_queue(&xprt->pending, "xprt_pending");
        rpc_init_wait_queue(&xprt->sending, "xprt_sending");
        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,
                        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;
 }
 
 /**
  * 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);
        xprt->shutdown = 1;
        del_timer_sync(&xprt->timer);
        xprt->ops->destroy(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;
 }
index 897bdd9..9b62923 100644 (file)
@@ -125,6 +125,47 @@ static inline void xs_pktdump(char *msg, u32 *packet, unsigned int count)
 }
 #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)
 
 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;
        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",
                        xdr->len - req->rq_bytes_sent, status);
@@ -485,6 +526,7 @@ static void xs_destroy(struct rpc_xprt *xprt)
 
        xprt_disconnect(xprt);
        xs_close(xprt);
+       xs_free_peer_addresses(xprt);
        kfree(xprt->slot);
 }
 
@@ -959,6 +1001,19 @@ static unsigned short xs_get_random_port(void)
        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
  * @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)
 {
+       struct sockaddr_in *sap = (struct sockaddr_in *) &xprt->addr;
+
        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)
@@ -1011,11 +1069,9 @@ static void xs_udp_connect_worker(void *args)
        struct socket *sock = xprt->sock;
        int err, status = -EIO;
 
-       if (xprt->shutdown || xprt->addr.sin_port == 0)
+       if (xprt->shutdown || !xprt_bound(xprt))
                goto out;
 
-       dprintk("RPC:      xs_udp_connect_worker for xprt %p\n", xprt);
-
        /* Start by resetting any existing state */
        xs_close(xprt);
 
@@ -1029,6 +1085,9 @@ static void xs_udp_connect_worker(void *args)
                goto out;
        }
 
+       dprintk("RPC:      worker connecting xprt %p to address: %s\n",
+                       xprt, xs_print_peer_address(xprt, RPC_DISPLAY_ALL));
+
        if (!xprt->inet) {
                struct sock *sk = sock->sk;
 
@@ -1094,11 +1153,9 @@ static void xs_tcp_connect_worker(void *args)
        struct socket *sock = xprt->sock;
        int err, status = -EIO;
 
-       if (xprt->shutdown || xprt->addr.sin_port == 0)
+       if (xprt->shutdown || !xprt_bound(xprt))
                goto out;
 
-       dprintk("RPC:      xs_tcp_connect_worker for xprt %p\n", xprt);
-
        if (!xprt->sock) {
                /* start from scratch */
                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 */
                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) {
                struct sock *sk = sock->sk;
 
@@ -1147,7 +1207,7 @@ static void xs_tcp_connect_worker(void *args)
        xprt->stat.connect_count++;
        xprt->stat.connect_start = jiffies;
        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",
                        xprt, -status, xprt_connected(xprt), sock->sk->sk_state);
        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 = {
        .set_buffer_size        = xs_udp_set_buffer_size,
+       .print_addr             = xs_print_peer_address,
        .reserve_xprt           = xprt_reserve_xprt_cong,
        .release_xprt           = xprt_release_xprt_cong,
+       .rpcbind                = rpc_getport,
        .set_port               = xs_set_port,
        .connect                = xs_connect,
        .buf_alloc              = rpc_malloc,
@@ -1271,8 +1333,10 @@ static struct rpc_xprt_ops xs_udp_ops = {
 };
 
 static struct rpc_xprt_ops xs_tcp_ops = {
+       .print_addr             = xs_print_peer_address,
        .reserve_xprt           = xprt_reserve_xprt,
        .release_xprt           = xs_tcp_release_xprt,
+       .rpcbind                = rpc_getport,
        .set_port               = xs_set_port,
        .connect                = xs_connect,
        .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)
 {
        size_t slot_table_size;
-
-       dprintk("RPC:      setting up udp-ipv4 transport...\n");
+       struct sockaddr_in *addr = (struct sockaddr_in *) &xprt->addr;
 
        xprt->max_reqs = xprt_udp_slot_table_entries;
        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)
                return -ENOMEM;
 
-       xprt->prot = IPPROTO_UDP;
+       if (ntohs(addr->sin_port != 0))
+               xprt_set_bound(xprt);
        xprt->port = xs_get_random_port();
+
+       xprt->prot = IPPROTO_UDP;
        xprt->tsh_size = 0;
-       xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0;
        /* XXX: header size can vary due to auth type, IPv6, etc. */
        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
                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;
 }
 
@@ -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)
 {
        size_t slot_table_size;
-
-       dprintk("RPC:      setting up tcp-ipv4 transport...\n");
+       struct sockaddr_in *addr = (struct sockaddr_in *) &xprt->addr;
 
        xprt->max_reqs = xprt_tcp_slot_table_entries;
        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)
                return -ENOMEM;
 
-       xprt->prot = IPPROTO_TCP;
+       if (ntohs(addr->sin_port) != 0)
+               xprt_set_bound(xprt);
        xprt->port = xs_get_random_port();
+
+       xprt->prot = IPPROTO_TCP;
        xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32);
-       xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0;
        xprt->max_payload = RPC_MAX_FRAGMENT_SIZE;
 
        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
                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;
 }