diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/Makefile | 6 | ||||
-rw-r--r-- | fs/nfs/callback.c | 9 | ||||
-rw-r--r-- | fs/nfs/callback_proc.c | 9 | ||||
-rw-r--r-- | fs/nfs/client.c | 312 | ||||
-rw-r--r-- | fs/nfs/delegation.c | 9 | ||||
-rw-r--r-- | fs/nfs/internal.h | 6 | ||||
-rw-r--r-- | fs/nfs/nfs4_fs.h | 52 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 2 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 128 | ||||
-rw-r--r-- | fs/nfs/super.c | 53 |
10 files changed, 364 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 | ||
5 | obj-$(CONFIG_NFS_FS) += nfs.o | 5 | obj-$(CONFIG_NFS_FS) += nfs.o |
6 | 6 | ||
7 | nfs-y := dir.o file.o inode.o super.o nfs2xdr.o pagelist.o \ | 7 | nfs-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 |
10 | nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o | 10 | nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o |
11 | nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o | 11 | nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o |
12 | nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o | 12 | nfs-$(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 | ||
167 | static int nfs_callback_authenticate(struct svc_rqst *rqstp) | 168 | static 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); |
50 | out_putclient: | 51 | out_putclient: |
51 | nfs4_put_client(clp); | 52 | nfs_put_client(clp); |
52 | out: | 53 | out: |
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); |
82 | out_putclient: | 83 | out_putclient: |
83 | nfs4_put_client(clp); | 84 | nfs_put_client(clp); |
84 | out: | 85 | out: |
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 | |||
49 | static DEFINE_SPINLOCK(nfs_client_lock); | ||
50 | static LIST_HEAD(nfs_client_list); | ||
51 | static 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 | */ | ||
59 | static 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 | |||
112 | error_3: | ||
113 | nfs_callback_down(); | ||
114 | __clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state); | ||
115 | error_2: | ||
116 | rpciod_down(); | ||
117 | __clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state); | ||
118 | error_1: | ||
119 | kfree(clp); | ||
120 | error_0: | ||
121 | return NULL; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * Destroy a shared client record | ||
126 | */ | ||
127 | static 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 | */ | ||
166 | void 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 | */ | ||
184 | static 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 | |||
203 | found: | ||
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 | */ | ||
212 | struct 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 | */ | ||
229 | struct 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 */ | ||
258 | install_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 | */ | ||
268 | found_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 | */ | ||
308 | void 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 | ||
22 | static struct nfs_delegation *nfs_alloc_delegation(void) | 23 | static 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 | } |
255 | out: | 256 | out: |
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 */ | ||
19 | extern void nfs_put_client(struct nfs_client *); | ||
20 | extern struct nfs_client *nfs_find_client(const struct sockaddr_in *, int); | ||
21 | extern struct nfs_client *nfs_get_client(const char *, const struct sockaddr_in *, int); | ||
22 | extern 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 |
20 | extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry); | 26 | extern 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 | */ | ||
48 | struct 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 */ |
240 | extern void init_nfsv4_state(struct nfs_server *); | 191 | extern void init_nfsv4_state(struct nfs_server *); |
241 | extern void destroy_nfsv4_state(struct nfs_server *); | 192 | extern void destroy_nfsv4_state(struct nfs_server *); |
242 | extern struct nfs_client *nfs4_get_client(struct in_addr *); | ||
243 | extern void nfs4_put_client(struct nfs_client *clp); | ||
244 | extern struct nfs_client *nfs4_find_client(struct in_addr *); | ||
245 | struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp); | 193 | struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp); |
246 | extern u32 nfs4_alloc_lockowner_id(struct nfs_client *); | 194 | extern 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 | ||
56 | const nfs4_stateid zero_stateid; | 57 | const nfs4_stateid zero_stateid; |
57 | 58 | ||
58 | static DEFINE_SPINLOCK(state_spinlock); | ||
59 | static LIST_HEAD(nfs4_clientid_list); | 59 | static LIST_HEAD(nfs4_clientid_list); |
60 | 60 | ||
61 | void | 61 | void |
@@ -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 | */ | ||
86 | static struct nfs_client * | ||
87 | nfs4_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 | |||
113 | static void | ||
114 | nfs4_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 | |||
133 | static 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 | |||
145 | struct 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 | |||
154 | struct nfs_client * | ||
155 | nfs4_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 | |||
182 | void | ||
183 | nfs4_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 | |||
195 | static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred) | 79 | static 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; |
976 | out_error: | 860 | out_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: | 1170 | client_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); |