NFS: Generalise the nfs_client structure
David Howells [Wed, 23 Aug 2006 00:06:10 +0000 (20:06 -0400)]
Generalise the nfs_client structure by:

 (1) Moving nfs_client to a more general place (nfs_fs_sb.h).

 (2) Renaming its maintenance routines to be non-NFS4 specific.

 (3) Move those maintenance routines to a new non-NFS4 specific file (client.c)
     and move the declarations to internal.h.

 (4) Make nfs_find/get_client() take a full sockaddr_in to include the port
     number (will be required for NFS2/3).

 (5) Make nfs_find/get_client() take the NFS protocol version (again will be
     required to differentiate NFS2, 3 & 4 client records).

Also:

 (6) Make nfs_client construction proceed akin to inodes, marking them as under
     construction and providing a function to indicate completion.

 (7) Make nfs_get_client() wait interruptibly if it finds a client that it can
     share, but that client is currently being constructed.

 (8) Make nfs4_create_client() use (6) and (7) instead of locking cl_sem.

Signed-Off-By: David Howells <dhowells@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

12 files changed:
fs/nfs/Makefile
fs/nfs/callback.c
fs/nfs/callback_proc.c
fs/nfs/client.c [new file with mode: 0644]
fs/nfs/delegation.c
fs/nfs/internal.h
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
fs/nfs/super.c
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h

index 0b572a0..3b993a6 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 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 1b596b6..a3ee113 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "nfs4_fs.h"
 #include "callback.h"
+#include "internal.h"
 
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
 
@@ -166,15 +167,15 @@ void nfs_callback_down(void)
 
 static int nfs_callback_authenticate(struct svc_rqst *rqstp)
 {
-       struct in_addr *addr = &rqstp->rq_addr.sin_addr;
+       struct sockaddr_in *addr = &rqstp->rq_addr;
        struct 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 55d6e2e..97cf8f7 100644 (file)
@@ -10,6 +10,7 @@
 #include "nfs4_fs.h"
 #include "callback.h"
 #include "delegation.h"
+#include "internal.h"
 
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
  
@@ -22,7 +23,7 @@ unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres
        
        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;
@@ -61,7 +62,7 @@ unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
        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..cb5e924
--- /dev/null
@@ -0,0 +1,312 @@
+/* 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 DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
+
+/*
+ * 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);
+               __set_bit(NFS_CS_RPCIOD, &clp->cl_res_state);
+               goto error_1;
+       }
+
+       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:
+       nfs_callback_down();
+       __clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
+error_2:
+       rpciod_down();
+       __clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state);
+error_1:
+       kfree(clp);
+error_0:
+       return NULL;
+}
+
+/*
+ * Destroy a shared client record
+ */
+static void nfs_free_client(struct nfs_client *clp)
+{
+       dprintk("--> nfs_free_client(%d)\n", clp->cl_nfsversion);
+
+#ifdef CONFIG_NFS_V4
+       if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) {
+               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));
+               nfs_idmap_delete(clp);
+       }
+#endif
+
+       /* -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)
+{
+       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->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
+ */
+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);
+       }
+
+       dprintk("--> nfs_get_client() = %p [share]\n", clp);
+       return clp;
+}
+
+/*
+ * Mark a server as ready or failed
+ */
+void nfs_mark_client_ready(struct nfs_client *clp, int state)
+{
+       clp->cl_cons_state = state;
+       wake_up_all(&nfs_client_active_wq);
+}
index cfe2397..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)
 {
@@ -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;
                }
        }
@@ -254,7 +255,7 @@ restart:
        }
 out:
        spin_unlock(&clp->cl_lock);
-       nfs4_put_client(clp);
+       nfs_put_client(clp);
        module_put_and_exit(0);
 }
 
@@ -266,10 +267,10 @@ void nfs_expire_all_delegations(struct nfs_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);
 }
 
index 4802157..ac370d5 100644 (file)
@@ -15,6 +15,12 @@ struct nfs_clone_mount {
        rpc_authflavor_t authflavor;
 };
 
+/* client.c */
+extern void nfs_put_client(struct nfs_client *);
+extern struct nfs_client *nfs_find_client(const struct sockaddr_in *, int);
+extern struct nfs_client *nfs_get_client(const char *, const struct sockaddr_in *, int);
+extern void nfs_mark_client_ready(struct nfs_client *, int);
+
 /* nfs4namespace.c */
 #ifdef CONFIG_NFS_V4
 extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry);
index 4e334cb..e787924 100644 (file)
@@ -43,55 +43,6 @@ enum nfs4_client_state {
 };
 
 /*
- * The nfs_client identifies our client state to the server.
- */
-struct nfs_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.
  */
@@ -239,9 +190,6 @@ 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 nfs_client *nfs4_get_client(struct in_addr *);
-extern void nfs4_put_client(struct nfs_client *clp);
-extern struct nfs_client *nfs4_find_client(struct in_addr *);
 struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp);
 extern u32 nfs4_alloc_lockowner_id(struct nfs_client *);
 
index 850f085..803c31b 100644 (file)
@@ -2968,7 +2968,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short po
        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,
index fa51a7d..058811e 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
@@ -71,127 +71,11 @@ destroy_nfsv4_state(struct nfs_server *server)
        kfree(server->mnt_path);
        server->mnt_path = NULL;
        if (server->nfs_client) {
-               nfs4_put_client(server->nfs_client);
+               nfs_put_client(server->nfs_client);
                server->nfs_client = 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 nfs_client *
-nfs4_alloc_client(struct in_addr *addr)
-{
-       struct nfs_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 nfs_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 nfs_client *__nfs4_find_client(struct in_addr *addr)
-{
-       struct nfs_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 nfs_client *nfs4_find_client(struct in_addr *addr)
-{
-       struct nfs_client *clp;
-       spin_lock(&state_spinlock);
-       clp = __nfs4_find_client(addr);
-       spin_unlock(&state_spinlock);
-       return clp;
-}
-
-struct nfs_client *
-nfs4_get_client(struct in_addr *addr)
-{
-       struct nfs_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 nfs_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 nfs_client *clp, struct rpc_cred *cred)
 {
        int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK,
@@ -771,11 +655,11 @@ static void nfs4_recover_state(struct nfs_client *clp)
        __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);
 }
 
@@ -970,12 +854,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 3ee85c4..f97d7d9 100644 (file)
@@ -1104,47 +1104,46 @@ static struct rpc_clnt *nfs4_create_client(struct nfs_server *server,
        struct rpc_clnt *clnt = NULL;
        int err = -EIO;
 
-       clp = nfs4_get_client(&server->addr.sin_addr);
+       clp = nfs_get_client(server->hostname, &server->addr, 4);
        if (!clp) {
                dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__);
                return ERR_PTR(err);
        }
 
        /* Now create transport and client */
-       down_write(&clp->cl_sem);
-       if (IS_ERR(clp->cl_rpcclient)) {
+       if (clp->cl_cons_state == NFS_CS_INITING) {
                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;
+                       goto client_init_error;
                }
                /* 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;
+                       goto client_init_error;
                }
                clnt->cl_intr     = 1;
                clnt->cl_softrtry = 1;
                clp->cl_rpcclient = clnt;
                memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr));
-               if (nfs_idmap_new(clp) < 0)
-                       goto out_fail;
+               err = nfs_idmap_new(clp);
+               if (err < 0) {
+                       dprintk("%s: failed to create idmapper.\n",
+                               __FUNCTION__);
+                       goto client_init_error;
+               }
+               __set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
+               nfs_mark_client_ready(clp, 0);
        }
-       list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
+
        clnt = rpc_clone_client(clp->cl_rpcclient);
-       if (!IS_ERR(clnt))
-               server->nfs_client = clp;
-       up_write(&clp->cl_sem);
-       clp = NULL;
 
        if (IS_ERR(clnt)) {
                dprintk("%s: cannot create RPC client. Error = %d\n",
@@ -1152,11 +1151,6 @@ static struct rpc_clnt *nfs4_create_client(struct nfs_server *server,
                return clnt;
        }
 
-       if (server->nfs_client->cl_idmap == NULL) {
-               dprintk("%s: failed to create idmapper.\n", __FUNCTION__);
-               return ERR_PTR(-ENOMEM);
-       }
-
        if (clnt->cl_auth->au_flavor != flavor) {
                struct rpc_auth *auth;
 
@@ -1166,11 +1160,16 @@ static struct rpc_clnt *nfs4_create_client(struct nfs_server *server,
                        return (struct rpc_clnt *)auth;
                }
        }
+
+       server->nfs_client = clp;
+       down_write(&clp->cl_sem);
+       list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
+       up_write(&clp->cl_sem);
        return clnt;
 
- out_fail:
-       if (clp)
-               nfs4_put_client(clp);
+client_init_error:
+       nfs_mark_client_ready(clp, err);
+       nfs_put_client(clp);
        return ERR_PTR(err);
 }
 
@@ -1329,14 +1328,6 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
                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;
-       }
-
        s = sget(fs_type, nfs4_compare_super, nfs_set_super, server);
        if (IS_ERR(s)) {
                error = PTR_ERR(s);
@@ -1383,8 +1374,6 @@ static void nfs4_kill_super(struct super_block *sb)
 
        destroy_nfsv4_state(server);
 
-       rpciod_down();
-
        nfs_free_iostats(server->io_stats);
        kfree(server->hostname);
        kfree(server);
index a36e01c..70e1dc9 100644 (file)
@@ -586,6 +586,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 fc20d6b..a727657 100644 (file)
@@ -7,6 +7,66 @@
 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 */
+       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;
+
+#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 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;
+#endif
+};
+
+/*
  * NFS client parameters stored in the superblock.
  */
 struct nfs_server {