aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/Makefile6
-rw-r--r--fs/nfs/callback.c9
-rw-r--r--fs/nfs/callback_proc.c9
-rw-r--r--fs/nfs/client.c312
-rw-r--r--fs/nfs/delegation.c9
-rw-r--r--fs/nfs/internal.h6
-rw-r--r--fs/nfs/nfs4_fs.h52
-rw-r--r--fs/nfs/nfs4proc.c2
-rw-r--r--fs/nfs/nfs4state.c128
-rw-r--r--fs/nfs/super.c53
-rw-r--r--include/linux/nfs_fs.h1
-rw-r--r--include/linux/nfs_fs_sb.h60
12 files changed, 425 insertions, 222 deletions
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index 0b572a0c1967..3b993a6f8163 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -4,9 +4,9 @@
4 4
5obj-$(CONFIG_NFS_FS) += nfs.o 5obj-$(CONFIG_NFS_FS) += nfs.o
6 6
7nfs-y := dir.o file.o inode.o super.o nfs2xdr.o pagelist.o \ 7nfs-y := client.o dir.o file.o inode.o super.o nfs2xdr.o \
8 proc.o read.o symlink.o unlink.o write.o \ 8 pagelist.o proc.o read.o symlink.o unlink.o \
9 namespace.o 9 write.o namespace.o
10nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o 10nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o
11nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o 11nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
12nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o 12nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index 1b596b6d9dc2..a3ee11364db0 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -19,6 +19,7 @@
19 19
20#include "nfs4_fs.h" 20#include "nfs4_fs.h"
21#include "callback.h" 21#include "callback.h"
22#include "internal.h"
22 23
23#define NFSDBG_FACILITY NFSDBG_CALLBACK 24#define NFSDBG_FACILITY NFSDBG_CALLBACK
24 25
@@ -166,15 +167,15 @@ void nfs_callback_down(void)
166 167
167static int nfs_callback_authenticate(struct svc_rqst *rqstp) 168static int nfs_callback_authenticate(struct svc_rqst *rqstp)
168{ 169{
169 struct in_addr *addr = &rqstp->rq_addr.sin_addr; 170 struct sockaddr_in *addr = &rqstp->rq_addr;
170 struct nfs_client *clp; 171 struct nfs_client *clp;
171 172
172 /* Don't talk to strangers */ 173 /* Don't talk to strangers */
173 clp = nfs4_find_client(addr); 174 clp = nfs_find_client(addr, 4);
174 if (clp == NULL) 175 if (clp == NULL)
175 return SVC_DROP; 176 return SVC_DROP;
176 dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr)); 177 dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr->sin_addr));
177 nfs4_put_client(clp); 178 nfs_put_client(clp);
178 switch (rqstp->rq_authop->flavour) { 179 switch (rqstp->rq_authop->flavour) {
179 case RPC_AUTH_NULL: 180 case RPC_AUTH_NULL:
180 if (rqstp->rq_proc != CB_NULL) 181 if (rqstp->rq_proc != CB_NULL)
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 55d6e2ec157f..97cf8f71451f 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -10,6 +10,7 @@
10#include "nfs4_fs.h" 10#include "nfs4_fs.h"
11#include "callback.h" 11#include "callback.h"
12#include "delegation.h" 12#include "delegation.h"
13#include "internal.h"
13 14
14#define NFSDBG_FACILITY NFSDBG_CALLBACK 15#define NFSDBG_FACILITY NFSDBG_CALLBACK
15 16
@@ -22,7 +23,7 @@ unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres
22 23
23 res->bitmap[0] = res->bitmap[1] = 0; 24 res->bitmap[0] = res->bitmap[1] = 0;
24 res->status = htonl(NFS4ERR_BADHANDLE); 25 res->status = htonl(NFS4ERR_BADHANDLE);
25 clp = nfs4_find_client(&args->addr->sin_addr); 26 clp = nfs_find_client(args->addr, 4);
26 if (clp == NULL) 27 if (clp == NULL)
27 goto out; 28 goto out;
28 inode = nfs_delegation_find_inode(clp, &args->fh); 29 inode = nfs_delegation_find_inode(clp, &args->fh);
@@ -48,7 +49,7 @@ out_iput:
48 up_read(&nfsi->rwsem); 49 up_read(&nfsi->rwsem);
49 iput(inode); 50 iput(inode);
50out_putclient: 51out_putclient:
51 nfs4_put_client(clp); 52 nfs_put_client(clp);
52out: 53out:
53 dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res->status)); 54 dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res->status));
54 return res->status; 55 return res->status;
@@ -61,7 +62,7 @@ unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
61 unsigned res; 62 unsigned res;
62 63
63 res = htonl(NFS4ERR_BADHANDLE); 64 res = htonl(NFS4ERR_BADHANDLE);
64 clp = nfs4_find_client(&args->addr->sin_addr); 65 clp = nfs_find_client(args->addr, 4);
65 if (clp == NULL) 66 if (clp == NULL)
66 goto out; 67 goto out;
67 inode = nfs_delegation_find_inode(clp, &args->fh); 68 inode = nfs_delegation_find_inode(clp, &args->fh);
@@ -80,7 +81,7 @@ unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
80 } 81 }
81 iput(inode); 82 iput(inode);
82out_putclient: 83out_putclient:
83 nfs4_put_client(clp); 84 nfs_put_client(clp);
84out: 85out:
85 dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res)); 86 dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res));
86 return res; 87 return res;
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
new file mode 100644
index 000000000000..cb5e92463bdb
--- /dev/null
+++ b/fs/nfs/client.c
@@ -0,0 +1,312 @@
1/* client.c: NFS client sharing and management code
2 *
3 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12
13#include <linux/config.h>
14#include <linux/module.h>
15#include <linux/init.h>
16
17#include <linux/time.h>
18#include <linux/kernel.h>
19#include <linux/mm.h>
20#include <linux/string.h>
21#include <linux/stat.h>
22#include <linux/errno.h>
23#include <linux/unistd.h>
24#include <linux/sunrpc/clnt.h>
25#include <linux/sunrpc/stats.h>
26#include <linux/sunrpc/metrics.h>
27#include <linux/nfs_fs.h>
28#include <linux/nfs_mount.h>
29#include <linux/nfs4_mount.h>
30#include <linux/lockd/bind.h>
31#include <linux/smp_lock.h>
32#include <linux/seq_file.h>
33#include <linux/mount.h>
34#include <linux/nfs_idmap.h>
35#include <linux/vfs.h>
36#include <linux/inet.h>
37#include <linux/nfs_xdr.h>
38
39#include <asm/system.h>
40
41#include "nfs4_fs.h"
42#include "callback.h"
43#include "delegation.h"
44#include "iostat.h"
45#include "internal.h"
46
47#define NFSDBG_FACILITY NFSDBG_CLIENT
48
49static DEFINE_SPINLOCK(nfs_client_lock);
50static LIST_HEAD(nfs_client_list);
51static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
52
53/*
54 * Allocate a shared client record
55 *
56 * Since these are allocated/deallocated very rarely, we don't
57 * bother putting them in a slab cache...
58 */
59static struct nfs_client *nfs_alloc_client(const char *hostname,
60 const struct sockaddr_in *addr,
61 int nfsversion)
62{
63 struct nfs_client *clp;
64 int error;
65
66 if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
67 goto error_0;
68
69 error = rpciod_up();
70 if (error < 0) {
71 dprintk("%s: couldn't start rpciod! Error = %d\n",
72 __FUNCTION__, error);
73 __set_bit(NFS_CS_RPCIOD, &clp->cl_res_state);
74 goto error_1;
75 }
76
77 if (nfsversion == 4) {
78 if (nfs_callback_up() < 0)
79 goto error_2;
80 __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
81 }
82
83 atomic_set(&clp->cl_count, 1);
84 clp->cl_cons_state = NFS_CS_INITING;
85
86 clp->cl_nfsversion = nfsversion;
87 memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
88
89 if (hostname) {
90 clp->cl_hostname = kstrdup(hostname, GFP_KERNEL);
91 if (!clp->cl_hostname)
92 goto error_3;
93 }
94
95 INIT_LIST_HEAD(&clp->cl_superblocks);
96 clp->cl_rpcclient = ERR_PTR(-EINVAL);
97
98#ifdef CONFIG_NFS_V4
99 init_rwsem(&clp->cl_sem);
100 INIT_LIST_HEAD(&clp->cl_delegations);
101 INIT_LIST_HEAD(&clp->cl_state_owners);
102 INIT_LIST_HEAD(&clp->cl_unused);
103 spin_lock_init(&clp->cl_lock);
104 INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
105 rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
106 clp->cl_boot_time = CURRENT_TIME;
107 clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
108#endif
109
110 return clp;
111
112error_3:
113 nfs_callback_down();
114 __clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
115error_2:
116 rpciod_down();
117 __clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state);
118error_1:
119 kfree(clp);
120error_0:
121 return NULL;
122}
123
124/*
125 * Destroy a shared client record
126 */
127static void nfs_free_client(struct nfs_client *clp)
128{
129 dprintk("--> nfs_free_client(%d)\n", clp->cl_nfsversion);
130
131#ifdef CONFIG_NFS_V4
132 if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) {
133 while (!list_empty(&clp->cl_unused)) {
134 struct nfs4_state_owner *sp;
135
136 sp = list_entry(clp->cl_unused.next,
137 struct nfs4_state_owner,
138 so_list);
139 list_del(&sp->so_list);
140 kfree(sp);
141 }
142 BUG_ON(!list_empty(&clp->cl_state_owners));
143 nfs_idmap_delete(clp);
144 }
145#endif
146
147 /* -EIO all pending I/O */
148 if (!IS_ERR(clp->cl_rpcclient))
149 rpc_shutdown_client(clp->cl_rpcclient);
150
151 if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
152 nfs_callback_down();
153
154 if (__test_and_clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state))
155 rpciod_down();
156
157 kfree(clp->cl_hostname);
158 kfree(clp);
159
160 dprintk("<-- nfs_free_client()\n");
161}
162
163/*
164 * Release a reference to a shared client record
165 */
166void nfs_put_client(struct nfs_client *clp)
167{
168 dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count));
169
170 if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) {
171 list_del(&clp->cl_share_link);
172 spin_unlock(&nfs_client_lock);
173
174 BUG_ON(!list_empty(&clp->cl_superblocks));
175
176 nfs_free_client(clp);
177 }
178}
179
180/*
181 * Find a client by address
182 * - caller must hold nfs_client_lock
183 */
184static struct nfs_client *__nfs_find_client(const struct sockaddr_in *addr, int nfsversion)
185{
186 struct nfs_client *clp;
187
188 list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
189 /* Different NFS versions cannot share the same nfs_client */
190 if (clp->cl_nfsversion != nfsversion)
191 continue;
192
193 if (memcmp(&clp->cl_addr.sin_addr, &addr->sin_addr,
194 sizeof(clp->cl_addr.sin_addr)) != 0)
195 continue;
196
197 if (clp->cl_addr.sin_port == addr->sin_port)
198 goto found;
199 }
200
201 return NULL;
202
203found:
204 atomic_inc(&clp->cl_count);
205 return clp;
206}
207
208/*
209 * Find a client by IP address and protocol version
210 * - returns NULL if no such client
211 */
212struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversion)
213{
214 struct nfs_client *clp;
215
216 spin_lock(&nfs_client_lock);
217 clp = __nfs_find_client(addr, nfsversion);
218 spin_unlock(&nfs_client_lock);
219
220 BUG_ON(clp->cl_cons_state == 0);
221
222 return clp;
223}
224
225/*
226 * Look up a client by IP address and protocol version
227 * - creates a new record if one doesn't yet exist
228 */
229struct nfs_client *nfs_get_client(const char *hostname,
230 const struct sockaddr_in *addr,
231 int nfsversion)
232{
233 struct nfs_client *clp, *new = NULL;
234 int error;
235
236 dprintk("--> nfs_get_client(%s,"NIPQUAD_FMT":%d,%d)\n",
237 hostname ?: "", NIPQUAD(addr->sin_addr),
238 addr->sin_port, nfsversion);
239
240 /* see if the client already exists */
241 do {
242 spin_lock(&nfs_client_lock);
243
244 clp = __nfs_find_client(addr, nfsversion);
245 if (clp)
246 goto found_client;
247 if (new)
248 goto install_client;
249
250 spin_unlock(&nfs_client_lock);
251
252 new = nfs_alloc_client(hostname, addr, nfsversion);
253 } while (new);
254
255 return ERR_PTR(-ENOMEM);
256
257 /* install a new client and return with it unready */
258install_client:
259 clp = new;
260 list_add(&clp->cl_share_link, &nfs_client_list);
261 spin_unlock(&nfs_client_lock);
262 dprintk("--> nfs_get_client() = %p [new]\n", clp);
263 return clp;
264
265 /* found an existing client
266 * - make sure it's ready before returning
267 */
268found_client:
269 spin_unlock(&nfs_client_lock);
270
271 if (new)
272 nfs_free_client(new);
273
274 if (clp->cl_cons_state == NFS_CS_INITING) {
275 DECLARE_WAITQUEUE(myself, current);
276
277 add_wait_queue(&nfs_client_active_wq, &myself);
278
279 for (;;) {
280 set_current_state(TASK_INTERRUPTIBLE);
281 if (signal_pending(current) ||
282 clp->cl_cons_state > NFS_CS_READY)
283 break;
284 schedule();
285 }
286
287 remove_wait_queue(&nfs_client_active_wq, &myself);
288
289 if (signal_pending(current)) {
290 nfs_put_client(clp);
291 return ERR_PTR(-ERESTARTSYS);
292 }
293 }
294
295 if (clp->cl_cons_state < NFS_CS_READY) {
296 error = clp->cl_cons_state;
297 nfs_put_client(clp);
298 return ERR_PTR(error);
299 }
300
301 dprintk("--> nfs_get_client() = %p [share]\n", clp);
302 return clp;
303}
304
305/*
306 * Mark a server as ready or failed
307 */
308void nfs_mark_client_ready(struct nfs_client *clp, int state)
309{
310 clp->cl_cons_state = state;
311 wake_up_all(&nfs_client_active_wq);
312}
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index cfe239736ac0..57133678db16 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -18,6 +18,7 @@
18 18
19#include "nfs4_fs.h" 19#include "nfs4_fs.h"
20#include "delegation.h" 20#include "delegation.h"
21#include "internal.h"
21 22
22static struct nfs_delegation *nfs_alloc_delegation(void) 23static struct nfs_delegation *nfs_alloc_delegation(void)
23{ 24{
@@ -145,7 +146,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
145 sizeof(delegation->stateid)) != 0 || 146 sizeof(delegation->stateid)) != 0 ||
146 delegation->type != nfsi->delegation->type) { 147 delegation->type != nfsi->delegation->type) {
147 printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n", 148 printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n",
148 __FUNCTION__, NIPQUAD(clp->cl_addr)); 149 __FUNCTION__, NIPQUAD(clp->cl_addr.sin_addr));
149 status = -EIO; 150 status = -EIO;
150 } 151 }
151 } 152 }
@@ -254,7 +255,7 @@ restart:
254 } 255 }
255out: 256out:
256 spin_unlock(&clp->cl_lock); 257 spin_unlock(&clp->cl_lock);
257 nfs4_put_client(clp); 258 nfs_put_client(clp);
258 module_put_and_exit(0); 259 module_put_and_exit(0);
259} 260}
260 261
@@ -266,10 +267,10 @@ void nfs_expire_all_delegations(struct nfs_client *clp)
266 atomic_inc(&clp->cl_count); 267 atomic_inc(&clp->cl_count);
267 task = kthread_run(nfs_do_expire_all_delegations, clp, 268 task = kthread_run(nfs_do_expire_all_delegations, clp,
268 "%u.%u.%u.%u-delegreturn", 269 "%u.%u.%u.%u-delegreturn",
269 NIPQUAD(clp->cl_addr)); 270 NIPQUAD(clp->cl_addr.sin_addr));
270 if (!IS_ERR(task)) 271 if (!IS_ERR(task))
271 return; 272 return;
272 nfs4_put_client(clp); 273 nfs_put_client(clp);
273 module_put(THIS_MODULE); 274 module_put(THIS_MODULE);
274} 275}
275 276
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 4802157963f8..ac370d5d4494 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -15,6 +15,12 @@ struct nfs_clone_mount {
15 rpc_authflavor_t authflavor; 15 rpc_authflavor_t authflavor;
16}; 16};
17 17
18/* client.c */
19extern void nfs_put_client(struct nfs_client *);
20extern struct nfs_client *nfs_find_client(const struct sockaddr_in *, int);
21extern struct nfs_client *nfs_get_client(const char *, const struct sockaddr_in *, int);
22extern void nfs_mark_client_ready(struct nfs_client *, int);
23
18/* nfs4namespace.c */ 24/* nfs4namespace.c */
19#ifdef CONFIG_NFS_V4 25#ifdef CONFIG_NFS_V4
20extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry); 26extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry);
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 4e334cb48498..e7879245361e 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -43,55 +43,6 @@ enum nfs4_client_state {
43}; 43};
44 44
45/* 45/*
46 * The nfs_client identifies our client state to the server.
47 */
48struct nfs_client {
49 struct list_head cl_servers; /* Global list of servers */
50 struct in_addr cl_addr; /* Server identifier */
51 u64 cl_clientid; /* constant */
52 nfs4_verifier cl_confirm;
53 unsigned long cl_state;
54
55 u32 cl_lockowner_id;
56
57 /*
58 * The following rwsem ensures exclusive access to the server
59 * while we recover the state following a lease expiration.
60 */
61 struct rw_semaphore cl_sem;
62
63 struct list_head cl_delegations;
64 struct list_head cl_state_owners;
65 struct list_head cl_unused;
66 int cl_nunused;
67 spinlock_t cl_lock;
68 atomic_t cl_count;
69
70 struct rpc_clnt * cl_rpcclient;
71
72 struct list_head cl_superblocks; /* List of nfs_server structs */
73
74 unsigned long cl_lease_time;
75 unsigned long cl_last_renewal;
76 struct work_struct cl_renewd;
77 struct work_struct cl_recoverd;
78
79 struct rpc_wait_queue cl_rpcwaitq;
80
81 /* used for the setclientid verifier */
82 struct timespec cl_boot_time;
83
84 /* idmapper */
85 struct idmap * cl_idmap;
86
87 /* Our own IP address, as a null-terminated string.
88 * This is used to generate the clientid, and the callback address.
89 */
90 char cl_ipaddr[16];
91 unsigned char cl_id_uniquifier;
92};
93
94/*
95 * struct rpc_sequence ensures that RPC calls are sent in the exact 46 * struct rpc_sequence ensures that RPC calls are sent in the exact
96 * order that they appear on the list. 47 * order that they appear on the list.
97 */ 48 */
@@ -239,9 +190,6 @@ extern void nfs4_renew_state(void *);
239/* nfs4state.c */ 190/* nfs4state.c */
240extern void init_nfsv4_state(struct nfs_server *); 191extern void init_nfsv4_state(struct nfs_server *);
241extern void destroy_nfsv4_state(struct nfs_server *); 192extern void destroy_nfsv4_state(struct nfs_server *);
242extern struct nfs_client *nfs4_get_client(struct in_addr *);
243extern void nfs4_put_client(struct nfs_client *clp);
244extern struct nfs_client *nfs4_find_client(struct in_addr *);
245struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp); 193struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp);
246extern u32 nfs4_alloc_lockowner_id(struct nfs_client *); 194extern u32 nfs4_alloc_lockowner_id(struct nfs_client *);
247 195
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 850f0851023a..803c31b88bb5 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2968,7 +2968,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short po
2968 for(;;) { 2968 for(;;) {
2969 setclientid.sc_name_len = scnprintf(setclientid.sc_name, 2969 setclientid.sc_name_len = scnprintf(setclientid.sc_name,
2970 sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u %s %u", 2970 sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u %s %u",
2971 clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr), 2971 clp->cl_ipaddr, NIPQUAD(clp->cl_addr.sin_addr),
2972 cred->cr_ops->cr_name, 2972 cred->cr_ops->cr_name,
2973 clp->cl_id_uniquifier); 2973 clp->cl_id_uniquifier);
2974 setclientid.sc_netid_len = scnprintf(setclientid.sc_netid, 2974 setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index fa51a7d4c022..058811e39555 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -50,12 +50,12 @@
50#include "nfs4_fs.h" 50#include "nfs4_fs.h"
51#include "callback.h" 51#include "callback.h"
52#include "delegation.h" 52#include "delegation.h"
53#include "internal.h"
53 54
54#define OPENOWNER_POOL_SIZE 8 55#define OPENOWNER_POOL_SIZE 8
55 56
56const nfs4_stateid zero_stateid; 57const nfs4_stateid zero_stateid;
57 58
58static DEFINE_SPINLOCK(state_spinlock);
59static LIST_HEAD(nfs4_clientid_list); 59static LIST_HEAD(nfs4_clientid_list);
60 60
61void 61void
@@ -71,127 +71,11 @@ destroy_nfsv4_state(struct nfs_server *server)
71 kfree(server->mnt_path); 71 kfree(server->mnt_path);
72 server->mnt_path = NULL; 72 server->mnt_path = NULL;
73 if (server->nfs_client) { 73 if (server->nfs_client) {
74 nfs4_put_client(server->nfs_client); 74 nfs_put_client(server->nfs_client);
75 server->nfs_client = NULL; 75 server->nfs_client = NULL;
76 } 76 }
77} 77}
78 78
79/*
80 * nfs4_get_client(): returns an empty client structure
81 * nfs4_put_client(): drops reference to client structure
82 *
83 * Since these are allocated/deallocated very rarely, we don't
84 * bother putting them in a slab cache...
85 */
86static struct nfs_client *
87nfs4_alloc_client(struct in_addr *addr)
88{
89 struct nfs_client *clp;
90
91 if (nfs_callback_up() < 0)
92 return NULL;
93 if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) {
94 nfs_callback_down();
95 return NULL;
96 }
97 memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
98 init_rwsem(&clp->cl_sem);
99 INIT_LIST_HEAD(&clp->cl_delegations);
100 INIT_LIST_HEAD(&clp->cl_state_owners);
101 INIT_LIST_HEAD(&clp->cl_unused);
102 spin_lock_init(&clp->cl_lock);
103 atomic_set(&clp->cl_count, 1);
104 INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
105 INIT_LIST_HEAD(&clp->cl_superblocks);
106 rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
107 clp->cl_rpcclient = ERR_PTR(-EINVAL);
108 clp->cl_boot_time = CURRENT_TIME;
109 clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
110 return clp;
111}
112
113static void
114nfs4_free_client(struct nfs_client *clp)
115{
116 struct nfs4_state_owner *sp;
117
118 while (!list_empty(&clp->cl_unused)) {
119 sp = list_entry(clp->cl_unused.next,
120 struct nfs4_state_owner,
121 so_list);
122 list_del(&sp->so_list);
123 kfree(sp);
124 }
125 BUG_ON(!list_empty(&clp->cl_state_owners));
126 nfs_idmap_delete(clp);
127 if (!IS_ERR(clp->cl_rpcclient))
128 rpc_shutdown_client(clp->cl_rpcclient);
129 kfree(clp);
130 nfs_callback_down();
131}
132
133static struct nfs_client *__nfs4_find_client(struct in_addr *addr)
134{
135 struct nfs_client *clp;
136 list_for_each_entry(clp, &nfs4_clientid_list, cl_servers) {
137 if (memcmp(&clp->cl_addr, addr, sizeof(clp->cl_addr)) == 0) {
138 atomic_inc(&clp->cl_count);
139 return clp;
140 }
141 }
142 return NULL;
143}
144
145struct nfs_client *nfs4_find_client(struct in_addr *addr)
146{
147 struct nfs_client *clp;
148 spin_lock(&state_spinlock);
149 clp = __nfs4_find_client(addr);
150 spin_unlock(&state_spinlock);
151 return clp;
152}
153
154struct nfs_client *
155nfs4_get_client(struct in_addr *addr)
156{
157 struct nfs_client *clp, *new = NULL;
158
159 spin_lock(&state_spinlock);
160 for (;;) {
161 clp = __nfs4_find_client(addr);
162 if (clp != NULL)
163 break;
164 clp = new;
165 if (clp != NULL) {
166 list_add(&clp->cl_servers, &nfs4_clientid_list);
167 new = NULL;
168 break;
169 }
170 spin_unlock(&state_spinlock);
171 new = nfs4_alloc_client(addr);
172 spin_lock(&state_spinlock);
173 if (new == NULL)
174 break;
175 }
176 spin_unlock(&state_spinlock);
177 if (new)
178 nfs4_free_client(new);
179 return clp;
180}
181
182void
183nfs4_put_client(struct nfs_client *clp)
184{
185 if (!atomic_dec_and_lock(&clp->cl_count, &state_spinlock))
186 return;
187 list_del(&clp->cl_servers);
188 spin_unlock(&state_spinlock);
189 BUG_ON(!list_empty(&clp->cl_superblocks));
190 rpc_wake_up(&clp->cl_rpcwaitq);
191 nfs4_kill_renewd(clp);
192 nfs4_free_client(clp);
193}
194
195static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred) 79static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
196{ 80{
197 int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, 81 int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK,
@@ -771,11 +655,11 @@ static void nfs4_recover_state(struct nfs_client *clp)
771 __module_get(THIS_MODULE); 655 __module_get(THIS_MODULE);
772 atomic_inc(&clp->cl_count); 656 atomic_inc(&clp->cl_count);
773 task = kthread_run(reclaimer, clp, "%u.%u.%u.%u-reclaim", 657 task = kthread_run(reclaimer, clp, "%u.%u.%u.%u-reclaim",
774 NIPQUAD(clp->cl_addr)); 658 NIPQUAD(clp->cl_addr.sin_addr));
775 if (!IS_ERR(task)) 659 if (!IS_ERR(task))
776 return; 660 return;
777 nfs4_clear_recover_bit(clp); 661 nfs4_clear_recover_bit(clp);
778 nfs4_put_client(clp); 662 nfs_put_client(clp);
779 module_put(THIS_MODULE); 663 module_put(THIS_MODULE);
780} 664}
781 665
@@ -970,12 +854,12 @@ out:
970 if (status == -NFS4ERR_CB_PATH_DOWN) 854 if (status == -NFS4ERR_CB_PATH_DOWN)
971 nfs_handle_cb_pathdown(clp); 855 nfs_handle_cb_pathdown(clp);
972 nfs4_clear_recover_bit(clp); 856 nfs4_clear_recover_bit(clp);
973 nfs4_put_client(clp); 857 nfs_put_client(clp);
974 module_put_and_exit(0); 858 module_put_and_exit(0);
975 return 0; 859 return 0;
976out_error: 860out_error:
977 printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n", 861 printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n",
978 NIPQUAD(clp->cl_addr.s_addr), -status); 862 NIPQUAD(clp->cl_addr.sin_addr), -status);
979 set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); 863 set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
980 goto out; 864 goto out;
981} 865}
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 3ee85c4e65d8..f97d7d9c5c32 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1104,47 +1104,46 @@ static struct rpc_clnt *nfs4_create_client(struct nfs_server *server,
1104 struct rpc_clnt *clnt = NULL; 1104 struct rpc_clnt *clnt = NULL;
1105 int err = -EIO; 1105 int err = -EIO;
1106 1106
1107 clp = nfs4_get_client(&server->addr.sin_addr); 1107 clp = nfs_get_client(server->hostname, &server->addr, 4);
1108 if (!clp) { 1108 if (!clp) {
1109 dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__); 1109 dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__);
1110 return ERR_PTR(err); 1110 return ERR_PTR(err);
1111 } 1111 }
1112 1112
1113 /* Now create transport and client */ 1113 /* Now create transport and client */
1114 down_write(&clp->cl_sem); 1114 if (clp->cl_cons_state == NFS_CS_INITING) {
1115 if (IS_ERR(clp->cl_rpcclient)) {
1116 xprt = xprt_create_proto(proto, &server->addr, timeparms); 1115 xprt = xprt_create_proto(proto, &server->addr, timeparms);
1117 if (IS_ERR(xprt)) { 1116 if (IS_ERR(xprt)) {
1118 up_write(&clp->cl_sem);
1119 err = PTR_ERR(xprt); 1117 err = PTR_ERR(xprt);
1120 dprintk("%s: cannot create RPC transport. Error = %d\n", 1118 dprintk("%s: cannot create RPC transport. Error = %d\n",
1121 __FUNCTION__, err); 1119 __FUNCTION__, err);
1122 goto out_fail; 1120 goto client_init_error;
1123 } 1121 }
1124 /* Bind to a reserved port! */ 1122 /* Bind to a reserved port! */
1125 xprt->resvport = 1; 1123 xprt->resvport = 1;
1126 clnt = rpc_create_client(xprt, server->hostname, &nfs_program, 1124 clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
1127 server->rpc_ops->version, flavor); 1125 server->rpc_ops->version, flavor);
1128 if (IS_ERR(clnt)) { 1126 if (IS_ERR(clnt)) {
1129 up_write(&clp->cl_sem);
1130 err = PTR_ERR(clnt); 1127 err = PTR_ERR(clnt);
1131 dprintk("%s: cannot create RPC client. Error = %d\n", 1128 dprintk("%s: cannot create RPC client. Error = %d\n",
1132 __FUNCTION__, err); 1129 __FUNCTION__, err);
1133 goto out_fail; 1130 goto client_init_error;
1134 } 1131 }
1135 clnt->cl_intr = 1; 1132 clnt->cl_intr = 1;
1136 clnt->cl_softrtry = 1; 1133 clnt->cl_softrtry = 1;
1137 clp->cl_rpcclient = clnt; 1134 clp->cl_rpcclient = clnt;
1138 memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); 1135 memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr));
1139 if (nfs_idmap_new(clp) < 0) 1136 err = nfs_idmap_new(clp);
1140 goto out_fail; 1137 if (err < 0) {
1138 dprintk("%s: failed to create idmapper.\n",
1139 __FUNCTION__);
1140 goto client_init_error;
1141 }
1142 __set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
1143 nfs_mark_client_ready(clp, 0);
1141 } 1144 }
1142 list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); 1145
1143 clnt = rpc_clone_client(clp->cl_rpcclient); 1146 clnt = rpc_clone_client(clp->cl_rpcclient);
1144 if (!IS_ERR(clnt))
1145 server->nfs_client = clp;
1146 up_write(&clp->cl_sem);
1147 clp = NULL;
1148 1147
1149 if (IS_ERR(clnt)) { 1148 if (IS_ERR(clnt)) {
1150 dprintk("%s: cannot create RPC client. Error = %d\n", 1149 dprintk("%s: cannot create RPC client. Error = %d\n",
@@ -1152,11 +1151,6 @@ static struct rpc_clnt *nfs4_create_client(struct nfs_server *server,
1152 return clnt; 1151 return clnt;
1153 } 1152 }
1154 1153
1155 if (server->nfs_client->cl_idmap == NULL) {
1156 dprintk("%s: failed to create idmapper.\n", __FUNCTION__);
1157 return ERR_PTR(-ENOMEM);
1158 }
1159
1160 if (clnt->cl_auth->au_flavor != flavor) { 1154 if (clnt->cl_auth->au_flavor != flavor) {
1161 struct rpc_auth *auth; 1155 struct rpc_auth *auth;
1162 1156
@@ -1166,11 +1160,16 @@ static struct rpc_clnt *nfs4_create_client(struct nfs_server *server,
1166 return (struct rpc_clnt *)auth; 1160 return (struct rpc_clnt *)auth;
1167 } 1161 }
1168 } 1162 }
1163
1164 server->nfs_client = clp;
1165 down_write(&clp->cl_sem);
1166 list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
1167 up_write(&clp->cl_sem);
1169 return clnt; 1168 return clnt;
1170 1169
1171 out_fail: 1170client_init_error:
1172 if (clp) 1171 nfs_mark_client_ready(clp, err);
1173 nfs4_put_client(clp); 1172 nfs_put_client(clp);
1174 return ERR_PTR(err); 1173 return ERR_PTR(err);
1175} 1174}
1176 1175
@@ -1329,14 +1328,6 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
1329 goto out_free; 1328 goto out_free;
1330 } 1329 }
1331 1330
1332 /* Fire up rpciod if not yet running */
1333 error = rpciod_up();
1334 if (error < 0) {
1335 dprintk("%s: couldn't start rpciod! Error = %d\n",
1336 __FUNCTION__, error);
1337 goto out_free;
1338 }
1339
1340 s = sget(fs_type, nfs4_compare_super, nfs_set_super, server); 1331 s = sget(fs_type, nfs4_compare_super, nfs_set_super, server);
1341 if (IS_ERR(s)) { 1332 if (IS_ERR(s)) {
1342 error = PTR_ERR(s); 1333 error = PTR_ERR(s);
@@ -1383,8 +1374,6 @@ static void nfs4_kill_super(struct super_block *sb)
1383 1374
1384 destroy_nfsv4_state(server); 1375 destroy_nfsv4_state(server);
1385 1376
1386 rpciod_down();
1387
1388 nfs_free_iostats(server->io_stats); 1377 nfs_free_iostats(server->io_stats);
1389 kfree(server->hostname); 1378 kfree(server->hostname);
1390 kfree(server); 1379 kfree(server);
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index a36e01cd6321..70e1dc9162e2 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -586,6 +586,7 @@ extern void * nfs_root_data(void);
586#define NFSDBG_FILE 0x0040 586#define NFSDBG_FILE 0x0040
587#define NFSDBG_ROOT 0x0080 587#define NFSDBG_ROOT 0x0080
588#define NFSDBG_CALLBACK 0x0100 588#define NFSDBG_CALLBACK 0x0100
589#define NFSDBG_CLIENT 0x0200
589#define NFSDBG_ALL 0xFFFF 590#define NFSDBG_ALL 0xFFFF
590 591
591#ifdef __KERNEL__ 592#ifdef __KERNEL__
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index fc20d6b934fb..a727657e0ad3 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -7,6 +7,66 @@
7struct nfs_iostats; 7struct nfs_iostats;
8 8
9/* 9/*
10 * The nfs_client identifies our client state to the server.
11 */
12struct nfs_client {
13 atomic_t cl_count;
14 int cl_cons_state; /* current construction state (-ve: init error) */
15#define NFS_CS_READY 0 /* ready to be used */
16#define NFS_CS_INITING 1 /* busy initialising */
17 int cl_nfsversion; /* NFS protocol version */
18 unsigned long cl_res_state; /* NFS resources state */
19#define NFS_CS_RPCIOD 0 /* - rpciod started */
20#define NFS_CS_CALLBACK 1 /* - callback started */
21#define NFS_CS_IDMAP 2 /* - idmap started */
22 struct sockaddr_in cl_addr; /* server identifier */
23 char * cl_hostname; /* hostname of server */
24 struct list_head cl_share_link; /* link in global client list */
25 struct list_head cl_superblocks; /* List of nfs_server structs */
26
27 struct rpc_clnt * cl_rpcclient;
28
29#ifdef CONFIG_NFS_V4
30 u64 cl_clientid; /* constant */
31 nfs4_verifier cl_confirm;
32 unsigned long cl_state;
33
34 u32 cl_lockowner_id;
35
36 /*
37 * The following rwsem ensures exclusive access to the server
38 * while we recover the state following a lease expiration.
39 */
40 struct rw_semaphore cl_sem;
41
42 struct list_head cl_delegations;
43 struct list_head cl_state_owners;
44 struct list_head cl_unused;
45 int cl_nunused;
46 spinlock_t cl_lock;
47
48 unsigned long cl_lease_time;
49 unsigned long cl_last_renewal;
50 struct work_struct cl_renewd;
51 struct work_struct cl_recoverd;
52
53 struct rpc_wait_queue cl_rpcwaitq;
54
55 /* used for the setclientid verifier */
56 struct timespec cl_boot_time;
57
58 /* idmapper */
59 struct idmap * cl_idmap;
60
61 /* Our own IP address, as a null-terminated string.
62 * This is used to generate the clientid, and the callback address.
63 */
64 char cl_ipaddr[16];
65 unsigned char cl_id_uniquifier;
66#endif
67};
68
69/*
10 * NFS client parameters stored in the superblock. 70 * NFS client parameters stored in the superblock.
11 */ 71 */
12struct nfs_server { 72struct nfs_server {