diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/nfs/nfs4state.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/nfs/nfs4state.c')
-rw-r--r-- | fs/nfs/nfs4state.c | 932 |
1 files changed, 932 insertions, 0 deletions
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c new file mode 100644 index 000000000000..231cebce3c87 --- /dev/null +++ b/fs/nfs/nfs4state.c | |||
@@ -0,0 +1,932 @@ | |||
1 | /* | ||
2 | * fs/nfs/nfs4state.c | ||
3 | * | ||
4 | * Client-side XDR for NFSv4. | ||
5 | * | ||
6 | * Copyright (c) 2002 The Regents of the University of Michigan. | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * Kendrick Smith <kmsmith@umich.edu> | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or without | ||
12 | * modification, are permitted provided that the following conditions | ||
13 | * are met: | ||
14 | * | ||
15 | * 1. Redistributions of source code must retain the above copyright | ||
16 | * notice, this list of conditions and the following disclaimer. | ||
17 | * 2. Redistributions in binary form must reproduce the above copyright | ||
18 | * notice, this list of conditions and the following disclaimer in the | ||
19 | * documentation and/or other materials provided with the distribution. | ||
20 | * 3. Neither the name of the University nor the names of its | ||
21 | * contributors may be used to endorse or promote products derived | ||
22 | * from this software without specific prior written permission. | ||
23 | * | ||
24 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
25 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
27 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
31 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
32 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
33 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
34 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
35 | * | ||
36 | * Implementation of the NFSv4 state model. For the time being, | ||
37 | * this is minimal, but will be made much more complex in a | ||
38 | * subsequent patch. | ||
39 | */ | ||
40 | |||
41 | #include <linux/config.h> | ||
42 | #include <linux/slab.h> | ||
43 | #include <linux/smp_lock.h> | ||
44 | #include <linux/nfs_fs.h> | ||
45 | #include <linux/nfs_idmap.h> | ||
46 | #include <linux/workqueue.h> | ||
47 | #include <linux/bitops.h> | ||
48 | |||
49 | #include "callback.h" | ||
50 | #include "delegation.h" | ||
51 | |||
52 | #define OPENOWNER_POOL_SIZE 8 | ||
53 | |||
54 | static DEFINE_SPINLOCK(state_spinlock); | ||
55 | |||
56 | nfs4_stateid zero_stateid; | ||
57 | |||
58 | #if 0 | ||
59 | nfs4_stateid one_stateid = | ||
60 | { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||
61 | #endif | ||
62 | |||
63 | static LIST_HEAD(nfs4_clientid_list); | ||
64 | |||
65 | static void nfs4_recover_state(void *); | ||
66 | extern void nfs4_renew_state(void *); | ||
67 | |||
68 | void | ||
69 | init_nfsv4_state(struct nfs_server *server) | ||
70 | { | ||
71 | server->nfs4_state = NULL; | ||
72 | INIT_LIST_HEAD(&server->nfs4_siblings); | ||
73 | } | ||
74 | |||
75 | void | ||
76 | destroy_nfsv4_state(struct nfs_server *server) | ||
77 | { | ||
78 | if (server->mnt_path) { | ||
79 | kfree(server->mnt_path); | ||
80 | server->mnt_path = NULL; | ||
81 | } | ||
82 | if (server->nfs4_state) { | ||
83 | nfs4_put_client(server->nfs4_state); | ||
84 | server->nfs4_state = NULL; | ||
85 | } | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * nfs4_get_client(): returns an empty client structure | ||
90 | * nfs4_put_client(): drops reference to client structure | ||
91 | * | ||
92 | * Since these are allocated/deallocated very rarely, we don't | ||
93 | * bother putting them in a slab cache... | ||
94 | */ | ||
95 | static struct nfs4_client * | ||
96 | nfs4_alloc_client(struct in_addr *addr) | ||
97 | { | ||
98 | struct nfs4_client *clp; | ||
99 | |||
100 | if (nfs_callback_up() < 0) | ||
101 | return NULL; | ||
102 | if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL)) == NULL) { | ||
103 | nfs_callback_down(); | ||
104 | return NULL; | ||
105 | } | ||
106 | memset(clp, 0, sizeof(*clp)); | ||
107 | memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr)); | ||
108 | init_rwsem(&clp->cl_sem); | ||
109 | INIT_LIST_HEAD(&clp->cl_delegations); | ||
110 | INIT_LIST_HEAD(&clp->cl_state_owners); | ||
111 | INIT_LIST_HEAD(&clp->cl_unused); | ||
112 | spin_lock_init(&clp->cl_lock); | ||
113 | atomic_set(&clp->cl_count, 1); | ||
114 | INIT_WORK(&clp->cl_recoverd, nfs4_recover_state, clp); | ||
115 | INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp); | ||
116 | INIT_LIST_HEAD(&clp->cl_superblocks); | ||
117 | init_waitqueue_head(&clp->cl_waitq); | ||
118 | rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client"); | ||
119 | clp->cl_boot_time = CURRENT_TIME; | ||
120 | clp->cl_state = 1 << NFS4CLNT_OK; | ||
121 | return clp; | ||
122 | } | ||
123 | |||
124 | static void | ||
125 | nfs4_free_client(struct nfs4_client *clp) | ||
126 | { | ||
127 | struct nfs4_state_owner *sp; | ||
128 | |||
129 | while (!list_empty(&clp->cl_unused)) { | ||
130 | sp = list_entry(clp->cl_unused.next, | ||
131 | struct nfs4_state_owner, | ||
132 | so_list); | ||
133 | list_del(&sp->so_list); | ||
134 | kfree(sp); | ||
135 | } | ||
136 | BUG_ON(!list_empty(&clp->cl_state_owners)); | ||
137 | if (clp->cl_cred) | ||
138 | put_rpccred(clp->cl_cred); | ||
139 | nfs_idmap_delete(clp); | ||
140 | if (clp->cl_rpcclient) | ||
141 | rpc_shutdown_client(clp->cl_rpcclient); | ||
142 | kfree(clp); | ||
143 | nfs_callback_down(); | ||
144 | } | ||
145 | |||
146 | static struct nfs4_client *__nfs4_find_client(struct in_addr *addr) | ||
147 | { | ||
148 | struct nfs4_client *clp; | ||
149 | list_for_each_entry(clp, &nfs4_clientid_list, cl_servers) { | ||
150 | if (memcmp(&clp->cl_addr, addr, sizeof(clp->cl_addr)) == 0) { | ||
151 | atomic_inc(&clp->cl_count); | ||
152 | return clp; | ||
153 | } | ||
154 | } | ||
155 | return NULL; | ||
156 | } | ||
157 | |||
158 | struct nfs4_client *nfs4_find_client(struct in_addr *addr) | ||
159 | { | ||
160 | struct nfs4_client *clp; | ||
161 | spin_lock(&state_spinlock); | ||
162 | clp = __nfs4_find_client(addr); | ||
163 | spin_unlock(&state_spinlock); | ||
164 | return clp; | ||
165 | } | ||
166 | |||
167 | struct nfs4_client * | ||
168 | nfs4_get_client(struct in_addr *addr) | ||
169 | { | ||
170 | struct nfs4_client *clp, *new = NULL; | ||
171 | |||
172 | spin_lock(&state_spinlock); | ||
173 | for (;;) { | ||
174 | clp = __nfs4_find_client(addr); | ||
175 | if (clp != NULL) | ||
176 | break; | ||
177 | clp = new; | ||
178 | if (clp != NULL) { | ||
179 | list_add(&clp->cl_servers, &nfs4_clientid_list); | ||
180 | new = NULL; | ||
181 | break; | ||
182 | } | ||
183 | spin_unlock(&state_spinlock); | ||
184 | new = nfs4_alloc_client(addr); | ||
185 | spin_lock(&state_spinlock); | ||
186 | if (new == NULL) | ||
187 | break; | ||
188 | } | ||
189 | spin_unlock(&state_spinlock); | ||
190 | if (new) | ||
191 | nfs4_free_client(new); | ||
192 | return clp; | ||
193 | } | ||
194 | |||
195 | void | ||
196 | nfs4_put_client(struct nfs4_client *clp) | ||
197 | { | ||
198 | if (!atomic_dec_and_lock(&clp->cl_count, &state_spinlock)) | ||
199 | return; | ||
200 | list_del(&clp->cl_servers); | ||
201 | spin_unlock(&state_spinlock); | ||
202 | BUG_ON(!list_empty(&clp->cl_superblocks)); | ||
203 | wake_up_all(&clp->cl_waitq); | ||
204 | rpc_wake_up(&clp->cl_rpcwaitq); | ||
205 | nfs4_kill_renewd(clp); | ||
206 | nfs4_free_client(clp); | ||
207 | } | ||
208 | |||
209 | static int __nfs4_init_client(struct nfs4_client *clp) | ||
210 | { | ||
211 | int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, nfs_callback_tcpport); | ||
212 | if (status == 0) | ||
213 | status = nfs4_proc_setclientid_confirm(clp); | ||
214 | if (status == 0) | ||
215 | nfs4_schedule_state_renewal(clp); | ||
216 | return status; | ||
217 | } | ||
218 | |||
219 | int nfs4_init_client(struct nfs4_client *clp) | ||
220 | { | ||
221 | return nfs4_map_errors(__nfs4_init_client(clp)); | ||
222 | } | ||
223 | |||
224 | u32 | ||
225 | nfs4_alloc_lockowner_id(struct nfs4_client *clp) | ||
226 | { | ||
227 | return clp->cl_lockowner_id ++; | ||
228 | } | ||
229 | |||
230 | static struct nfs4_state_owner * | ||
231 | nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred) | ||
232 | { | ||
233 | struct nfs4_state_owner *sp = NULL; | ||
234 | |||
235 | if (!list_empty(&clp->cl_unused)) { | ||
236 | sp = list_entry(clp->cl_unused.next, struct nfs4_state_owner, so_list); | ||
237 | atomic_inc(&sp->so_count); | ||
238 | sp->so_cred = cred; | ||
239 | list_move(&sp->so_list, &clp->cl_state_owners); | ||
240 | clp->cl_nunused--; | ||
241 | } | ||
242 | return sp; | ||
243 | } | ||
244 | |||
245 | static struct nfs4_state_owner * | ||
246 | nfs4_find_state_owner(struct nfs4_client *clp, struct rpc_cred *cred) | ||
247 | { | ||
248 | struct nfs4_state_owner *sp, *res = NULL; | ||
249 | |||
250 | list_for_each_entry(sp, &clp->cl_state_owners, so_list) { | ||
251 | if (sp->so_cred != cred) | ||
252 | continue; | ||
253 | atomic_inc(&sp->so_count); | ||
254 | /* Move to the head of the list */ | ||
255 | list_move(&sp->so_list, &clp->cl_state_owners); | ||
256 | res = sp; | ||
257 | break; | ||
258 | } | ||
259 | return res; | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to | ||
264 | * create a new state_owner. | ||
265 | * | ||
266 | */ | ||
267 | static struct nfs4_state_owner * | ||
268 | nfs4_alloc_state_owner(void) | ||
269 | { | ||
270 | struct nfs4_state_owner *sp; | ||
271 | |||
272 | sp = kmalloc(sizeof(*sp),GFP_KERNEL); | ||
273 | if (!sp) | ||
274 | return NULL; | ||
275 | init_MUTEX(&sp->so_sema); | ||
276 | sp->so_seqid = 0; /* arbitrary */ | ||
277 | INIT_LIST_HEAD(&sp->so_states); | ||
278 | INIT_LIST_HEAD(&sp->so_delegations); | ||
279 | atomic_set(&sp->so_count, 1); | ||
280 | return sp; | ||
281 | } | ||
282 | |||
283 | void | ||
284 | nfs4_drop_state_owner(struct nfs4_state_owner *sp) | ||
285 | { | ||
286 | struct nfs4_client *clp = sp->so_client; | ||
287 | spin_lock(&clp->cl_lock); | ||
288 | list_del_init(&sp->so_list); | ||
289 | spin_unlock(&clp->cl_lock); | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * Note: must be called with clp->cl_sem held in order to prevent races | ||
294 | * with reboot recovery! | ||
295 | */ | ||
296 | struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) | ||
297 | { | ||
298 | struct nfs4_client *clp = server->nfs4_state; | ||
299 | struct nfs4_state_owner *sp, *new; | ||
300 | |||
301 | get_rpccred(cred); | ||
302 | new = nfs4_alloc_state_owner(); | ||
303 | spin_lock(&clp->cl_lock); | ||
304 | sp = nfs4_find_state_owner(clp, cred); | ||
305 | if (sp == NULL) | ||
306 | sp = nfs4_client_grab_unused(clp, cred); | ||
307 | if (sp == NULL && new != NULL) { | ||
308 | list_add(&new->so_list, &clp->cl_state_owners); | ||
309 | new->so_client = clp; | ||
310 | new->so_id = nfs4_alloc_lockowner_id(clp); | ||
311 | new->so_cred = cred; | ||
312 | sp = new; | ||
313 | new = NULL; | ||
314 | } | ||
315 | spin_unlock(&clp->cl_lock); | ||
316 | if (new) | ||
317 | kfree(new); | ||
318 | if (sp != NULL) | ||
319 | return sp; | ||
320 | put_rpccred(cred); | ||
321 | return NULL; | ||
322 | } | ||
323 | |||
324 | /* | ||
325 | * Must be called with clp->cl_sem held in order to avoid races | ||
326 | * with state recovery... | ||
327 | */ | ||
328 | void nfs4_put_state_owner(struct nfs4_state_owner *sp) | ||
329 | { | ||
330 | struct nfs4_client *clp = sp->so_client; | ||
331 | struct rpc_cred *cred = sp->so_cred; | ||
332 | |||
333 | if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) | ||
334 | return; | ||
335 | if (clp->cl_nunused >= OPENOWNER_POOL_SIZE) | ||
336 | goto out_free; | ||
337 | if (list_empty(&sp->so_list)) | ||
338 | goto out_free; | ||
339 | list_move(&sp->so_list, &clp->cl_unused); | ||
340 | clp->cl_nunused++; | ||
341 | spin_unlock(&clp->cl_lock); | ||
342 | put_rpccred(cred); | ||
343 | cred = NULL; | ||
344 | return; | ||
345 | out_free: | ||
346 | list_del(&sp->so_list); | ||
347 | spin_unlock(&clp->cl_lock); | ||
348 | put_rpccred(cred); | ||
349 | kfree(sp); | ||
350 | } | ||
351 | |||
352 | static struct nfs4_state * | ||
353 | nfs4_alloc_open_state(void) | ||
354 | { | ||
355 | struct nfs4_state *state; | ||
356 | |||
357 | state = kmalloc(sizeof(*state), GFP_KERNEL); | ||
358 | if (!state) | ||
359 | return NULL; | ||
360 | state->state = 0; | ||
361 | state->nreaders = 0; | ||
362 | state->nwriters = 0; | ||
363 | state->flags = 0; | ||
364 | memset(state->stateid.data, 0, sizeof(state->stateid.data)); | ||
365 | atomic_set(&state->count, 1); | ||
366 | INIT_LIST_HEAD(&state->lock_states); | ||
367 | init_MUTEX(&state->lock_sema); | ||
368 | rwlock_init(&state->state_lock); | ||
369 | return state; | ||
370 | } | ||
371 | |||
372 | static struct nfs4_state * | ||
373 | __nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode) | ||
374 | { | ||
375 | struct nfs_inode *nfsi = NFS_I(inode); | ||
376 | struct nfs4_state *state; | ||
377 | |||
378 | mode &= (FMODE_READ|FMODE_WRITE); | ||
379 | list_for_each_entry(state, &nfsi->open_states, inode_states) { | ||
380 | if (state->owner->so_cred != cred) | ||
381 | continue; | ||
382 | if ((mode & FMODE_READ) != 0 && state->nreaders == 0) | ||
383 | continue; | ||
384 | if ((mode & FMODE_WRITE) != 0 && state->nwriters == 0) | ||
385 | continue; | ||
386 | if ((state->state & mode) != mode) | ||
387 | continue; | ||
388 | atomic_inc(&state->count); | ||
389 | if (mode & FMODE_READ) | ||
390 | state->nreaders++; | ||
391 | if (mode & FMODE_WRITE) | ||
392 | state->nwriters++; | ||
393 | return state; | ||
394 | } | ||
395 | return NULL; | ||
396 | } | ||
397 | |||
398 | static struct nfs4_state * | ||
399 | __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) | ||
400 | { | ||
401 | struct nfs_inode *nfsi = NFS_I(inode); | ||
402 | struct nfs4_state *state; | ||
403 | |||
404 | list_for_each_entry(state, &nfsi->open_states, inode_states) { | ||
405 | /* Is this in the process of being freed? */ | ||
406 | if (state->nreaders == 0 && state->nwriters == 0) | ||
407 | continue; | ||
408 | if (state->owner == owner) { | ||
409 | atomic_inc(&state->count); | ||
410 | return state; | ||
411 | } | ||
412 | } | ||
413 | return NULL; | ||
414 | } | ||
415 | |||
416 | struct nfs4_state * | ||
417 | nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode) | ||
418 | { | ||
419 | struct nfs4_state *state; | ||
420 | |||
421 | spin_lock(&inode->i_lock); | ||
422 | state = __nfs4_find_state(inode, cred, mode); | ||
423 | spin_unlock(&inode->i_lock); | ||
424 | return state; | ||
425 | } | ||
426 | |||
427 | static void | ||
428 | nfs4_free_open_state(struct nfs4_state *state) | ||
429 | { | ||
430 | kfree(state); | ||
431 | } | ||
432 | |||
433 | struct nfs4_state * | ||
434 | nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner) | ||
435 | { | ||
436 | struct nfs4_state *state, *new; | ||
437 | struct nfs_inode *nfsi = NFS_I(inode); | ||
438 | |||
439 | spin_lock(&inode->i_lock); | ||
440 | state = __nfs4_find_state_byowner(inode, owner); | ||
441 | spin_unlock(&inode->i_lock); | ||
442 | if (state) | ||
443 | goto out; | ||
444 | new = nfs4_alloc_open_state(); | ||
445 | spin_lock(&inode->i_lock); | ||
446 | state = __nfs4_find_state_byowner(inode, owner); | ||
447 | if (state == NULL && new != NULL) { | ||
448 | state = new; | ||
449 | /* Caller *must* be holding owner->so_sem */ | ||
450 | /* Note: The reclaim code dictates that we add stateless | ||
451 | * and read-only stateids to the end of the list */ | ||
452 | list_add_tail(&state->open_states, &owner->so_states); | ||
453 | state->owner = owner; | ||
454 | atomic_inc(&owner->so_count); | ||
455 | list_add(&state->inode_states, &nfsi->open_states); | ||
456 | state->inode = igrab(inode); | ||
457 | spin_unlock(&inode->i_lock); | ||
458 | } else { | ||
459 | spin_unlock(&inode->i_lock); | ||
460 | if (new) | ||
461 | nfs4_free_open_state(new); | ||
462 | } | ||
463 | out: | ||
464 | return state; | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * Beware! Caller must be holding exactly one | ||
469 | * reference to clp->cl_sem and owner->so_sema! | ||
470 | */ | ||
471 | void nfs4_put_open_state(struct nfs4_state *state) | ||
472 | { | ||
473 | struct inode *inode = state->inode; | ||
474 | struct nfs4_state_owner *owner = state->owner; | ||
475 | |||
476 | if (!atomic_dec_and_lock(&state->count, &inode->i_lock)) | ||
477 | return; | ||
478 | if (!list_empty(&state->inode_states)) | ||
479 | list_del(&state->inode_states); | ||
480 | spin_unlock(&inode->i_lock); | ||
481 | list_del(&state->open_states); | ||
482 | iput(inode); | ||
483 | BUG_ON (state->state != 0); | ||
484 | nfs4_free_open_state(state); | ||
485 | nfs4_put_state_owner(owner); | ||
486 | } | ||
487 | |||
488 | /* | ||
489 | * Beware! Caller must be holding no references to clp->cl_sem! | ||
490 | * of owner->so_sema! | ||
491 | */ | ||
492 | void nfs4_close_state(struct nfs4_state *state, mode_t mode) | ||
493 | { | ||
494 | struct inode *inode = state->inode; | ||
495 | struct nfs4_state_owner *owner = state->owner; | ||
496 | struct nfs4_client *clp = owner->so_client; | ||
497 | int newstate; | ||
498 | |||
499 | atomic_inc(&owner->so_count); | ||
500 | down_read(&clp->cl_sem); | ||
501 | down(&owner->so_sema); | ||
502 | /* Protect against nfs4_find_state() */ | ||
503 | spin_lock(&inode->i_lock); | ||
504 | if (mode & FMODE_READ) | ||
505 | state->nreaders--; | ||
506 | if (mode & FMODE_WRITE) | ||
507 | state->nwriters--; | ||
508 | if (state->nwriters == 0) { | ||
509 | if (state->nreaders == 0) | ||
510 | list_del_init(&state->inode_states); | ||
511 | /* See reclaim code */ | ||
512 | list_move_tail(&state->open_states, &owner->so_states); | ||
513 | } | ||
514 | spin_unlock(&inode->i_lock); | ||
515 | newstate = 0; | ||
516 | if (state->state != 0) { | ||
517 | if (state->nreaders) | ||
518 | newstate |= FMODE_READ; | ||
519 | if (state->nwriters) | ||
520 | newstate |= FMODE_WRITE; | ||
521 | if (state->state == newstate) | ||
522 | goto out; | ||
523 | if (nfs4_do_close(inode, state, newstate) == -EINPROGRESS) | ||
524 | return; | ||
525 | } | ||
526 | out: | ||
527 | nfs4_put_open_state(state); | ||
528 | up(&owner->so_sema); | ||
529 | nfs4_put_state_owner(owner); | ||
530 | up_read(&clp->cl_sem); | ||
531 | } | ||
532 | |||
533 | /* | ||
534 | * Search the state->lock_states for an existing lock_owner | ||
535 | * that is compatible with current->files | ||
536 | */ | ||
537 | static struct nfs4_lock_state * | ||
538 | __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) | ||
539 | { | ||
540 | struct nfs4_lock_state *pos; | ||
541 | list_for_each_entry(pos, &state->lock_states, ls_locks) { | ||
542 | if (pos->ls_owner != fl_owner) | ||
543 | continue; | ||
544 | atomic_inc(&pos->ls_count); | ||
545 | return pos; | ||
546 | } | ||
547 | return NULL; | ||
548 | } | ||
549 | |||
550 | struct nfs4_lock_state * | ||
551 | nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) | ||
552 | { | ||
553 | struct nfs4_lock_state *lsp; | ||
554 | read_lock(&state->state_lock); | ||
555 | lsp = __nfs4_find_lock_state(state, fl_owner); | ||
556 | read_unlock(&state->state_lock); | ||
557 | return lsp; | ||
558 | } | ||
559 | |||
560 | /* | ||
561 | * Return a compatible lock_state. If no initialized lock_state structure | ||
562 | * exists, return an uninitialized one. | ||
563 | * | ||
564 | * The caller must be holding state->lock_sema | ||
565 | */ | ||
566 | static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) | ||
567 | { | ||
568 | struct nfs4_lock_state *lsp; | ||
569 | struct nfs4_client *clp = state->owner->so_client; | ||
570 | |||
571 | lsp = kmalloc(sizeof(*lsp), GFP_KERNEL); | ||
572 | if (lsp == NULL) | ||
573 | return NULL; | ||
574 | lsp->ls_flags = 0; | ||
575 | lsp->ls_seqid = 0; /* arbitrary */ | ||
576 | lsp->ls_id = -1; | ||
577 | memset(lsp->ls_stateid.data, 0, sizeof(lsp->ls_stateid.data)); | ||
578 | atomic_set(&lsp->ls_count, 1); | ||
579 | lsp->ls_owner = fl_owner; | ||
580 | INIT_LIST_HEAD(&lsp->ls_locks); | ||
581 | spin_lock(&clp->cl_lock); | ||
582 | lsp->ls_id = nfs4_alloc_lockowner_id(clp); | ||
583 | spin_unlock(&clp->cl_lock); | ||
584 | return lsp; | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | * Return a compatible lock_state. If no initialized lock_state structure | ||
589 | * exists, return an uninitialized one. | ||
590 | * | ||
591 | * The caller must be holding state->lock_sema and clp->cl_sem | ||
592 | */ | ||
593 | struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner) | ||
594 | { | ||
595 | struct nfs4_lock_state * lsp; | ||
596 | |||
597 | lsp = nfs4_find_lock_state(state, owner); | ||
598 | if (lsp == NULL) | ||
599 | lsp = nfs4_alloc_lock_state(state, owner); | ||
600 | return lsp; | ||
601 | } | ||
602 | |||
603 | /* | ||
604 | * Byte-range lock aware utility to initialize the stateid of read/write | ||
605 | * requests. | ||
606 | */ | ||
607 | void | ||
608 | nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner) | ||
609 | { | ||
610 | if (test_bit(LK_STATE_IN_USE, &state->flags)) { | ||
611 | struct nfs4_lock_state *lsp; | ||
612 | |||
613 | lsp = nfs4_find_lock_state(state, fl_owner); | ||
614 | if (lsp) { | ||
615 | memcpy(dst, &lsp->ls_stateid, sizeof(*dst)); | ||
616 | nfs4_put_lock_state(lsp); | ||
617 | return; | ||
618 | } | ||
619 | } | ||
620 | memcpy(dst, &state->stateid, sizeof(*dst)); | ||
621 | } | ||
622 | |||
623 | /* | ||
624 | * Called with state->lock_sema and clp->cl_sem held. | ||
625 | */ | ||
626 | void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp) | ||
627 | { | ||
628 | if (status == NFS_OK || seqid_mutating_err(-status)) | ||
629 | lsp->ls_seqid++; | ||
630 | } | ||
631 | |||
632 | /* | ||
633 | * Check to see if the request lock (type FL_UNLK) effects the fl lock. | ||
634 | * | ||
635 | * fl and request must have the same posix owner | ||
636 | * | ||
637 | * return: | ||
638 | * 0 -> fl not effected by request | ||
639 | * 1 -> fl consumed by request | ||
640 | */ | ||
641 | |||
642 | static int | ||
643 | nfs4_check_unlock(struct file_lock *fl, struct file_lock *request) | ||
644 | { | ||
645 | if (fl->fl_start >= request->fl_start && fl->fl_end <= request->fl_end) | ||
646 | return 1; | ||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | /* | ||
651 | * Post an initialized lock_state on the state->lock_states list. | ||
652 | */ | ||
653 | void nfs4_notify_setlk(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp) | ||
654 | { | ||
655 | if (!list_empty(&lsp->ls_locks)) | ||
656 | return; | ||
657 | atomic_inc(&lsp->ls_count); | ||
658 | write_lock(&state->state_lock); | ||
659 | list_add(&lsp->ls_locks, &state->lock_states); | ||
660 | set_bit(LK_STATE_IN_USE, &state->flags); | ||
661 | write_unlock(&state->state_lock); | ||
662 | } | ||
663 | |||
664 | /* | ||
665 | * to decide to 'reap' lock state: | ||
666 | * 1) search i_flock for file_locks with fl.lock_state = to ls. | ||
667 | * 2) determine if unlock will consume found lock. | ||
668 | * if so, reap | ||
669 | * | ||
670 | * else, don't reap. | ||
671 | * | ||
672 | */ | ||
673 | void | ||
674 | nfs4_notify_unlck(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp) | ||
675 | { | ||
676 | struct inode *inode = state->inode; | ||
677 | struct file_lock *fl; | ||
678 | |||
679 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { | ||
680 | if (!(fl->fl_flags & FL_POSIX)) | ||
681 | continue; | ||
682 | if (fl->fl_owner != lsp->ls_owner) | ||
683 | continue; | ||
684 | /* Exit if we find at least one lock which is not consumed */ | ||
685 | if (nfs4_check_unlock(fl,request) == 0) | ||
686 | return; | ||
687 | } | ||
688 | |||
689 | write_lock(&state->state_lock); | ||
690 | list_del_init(&lsp->ls_locks); | ||
691 | if (list_empty(&state->lock_states)) | ||
692 | clear_bit(LK_STATE_IN_USE, &state->flags); | ||
693 | write_unlock(&state->state_lock); | ||
694 | nfs4_put_lock_state(lsp); | ||
695 | } | ||
696 | |||
697 | /* | ||
698 | * Release reference to lock_state, and free it if we see that | ||
699 | * it is no longer in use | ||
700 | */ | ||
701 | void | ||
702 | nfs4_put_lock_state(struct nfs4_lock_state *lsp) | ||
703 | { | ||
704 | if (!atomic_dec_and_test(&lsp->ls_count)) | ||
705 | return; | ||
706 | BUG_ON (!list_empty(&lsp->ls_locks)); | ||
707 | kfree(lsp); | ||
708 | } | ||
709 | |||
710 | /* | ||
711 | * Called with sp->so_sema and clp->cl_sem held. | ||
712 | * | ||
713 | * Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or | ||
714 | * failed with a seqid incrementing error - | ||
715 | * see comments nfs_fs.h:seqid_mutating_error() | ||
716 | */ | ||
717 | void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp) | ||
718 | { | ||
719 | if (status == NFS_OK || seqid_mutating_err(-status)) | ||
720 | sp->so_seqid++; | ||
721 | /* If the server returns BAD_SEQID, unhash state_owner here */ | ||
722 | if (status == -NFS4ERR_BAD_SEQID) | ||
723 | nfs4_drop_state_owner(sp); | ||
724 | } | ||
725 | |||
726 | static int reclaimer(void *); | ||
727 | struct reclaimer_args { | ||
728 | struct nfs4_client *clp; | ||
729 | struct completion complete; | ||
730 | }; | ||
731 | |||
732 | /* | ||
733 | * State recovery routine | ||
734 | */ | ||
735 | void | ||
736 | nfs4_recover_state(void *data) | ||
737 | { | ||
738 | struct nfs4_client *clp = (struct nfs4_client *)data; | ||
739 | struct reclaimer_args args = { | ||
740 | .clp = clp, | ||
741 | }; | ||
742 | might_sleep(); | ||
743 | |||
744 | init_completion(&args.complete); | ||
745 | |||
746 | if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) | ||
747 | goto out_failed_clear; | ||
748 | wait_for_completion(&args.complete); | ||
749 | return; | ||
750 | out_failed_clear: | ||
751 | set_bit(NFS4CLNT_OK, &clp->cl_state); | ||
752 | wake_up_all(&clp->cl_waitq); | ||
753 | rpc_wake_up(&clp->cl_rpcwaitq); | ||
754 | } | ||
755 | |||
756 | /* | ||
757 | * Schedule a state recovery attempt | ||
758 | */ | ||
759 | void | ||
760 | nfs4_schedule_state_recovery(struct nfs4_client *clp) | ||
761 | { | ||
762 | if (!clp) | ||
763 | return; | ||
764 | if (test_and_clear_bit(NFS4CLNT_OK, &clp->cl_state)) | ||
765 | schedule_work(&clp->cl_recoverd); | ||
766 | } | ||
767 | |||
768 | static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_state *state) | ||
769 | { | ||
770 | struct inode *inode = state->inode; | ||
771 | struct file_lock *fl; | ||
772 | int status = 0; | ||
773 | |||
774 | for (fl = inode->i_flock; fl != 0; fl = fl->fl_next) { | ||
775 | if (!(fl->fl_flags & FL_POSIX)) | ||
776 | continue; | ||
777 | if (((struct nfs_open_context *)fl->fl_file->private_data)->state != state) | ||
778 | continue; | ||
779 | status = ops->recover_lock(state, fl); | ||
780 | if (status >= 0) | ||
781 | continue; | ||
782 | switch (status) { | ||
783 | default: | ||
784 | printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", | ||
785 | __FUNCTION__, status); | ||
786 | case -NFS4ERR_EXPIRED: | ||
787 | case -NFS4ERR_NO_GRACE: | ||
788 | case -NFS4ERR_RECLAIM_BAD: | ||
789 | case -NFS4ERR_RECLAIM_CONFLICT: | ||
790 | /* kill_proc(fl->fl_owner, SIGLOST, 1); */ | ||
791 | break; | ||
792 | case -NFS4ERR_STALE_CLIENTID: | ||
793 | goto out_err; | ||
794 | } | ||
795 | } | ||
796 | return 0; | ||
797 | out_err: | ||
798 | return status; | ||
799 | } | ||
800 | |||
801 | static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct nfs4_state_owner *sp) | ||
802 | { | ||
803 | struct nfs4_state *state; | ||
804 | struct nfs4_lock_state *lock; | ||
805 | int status = 0; | ||
806 | |||
807 | /* Note: we rely on the sp->so_states list being ordered | ||
808 | * so that we always reclaim open(O_RDWR) and/or open(O_WRITE) | ||
809 | * states first. | ||
810 | * This is needed to ensure that the server won't give us any | ||
811 | * read delegations that we have to return if, say, we are | ||
812 | * recovering after a network partition or a reboot from a | ||
813 | * server that doesn't support a grace period. | ||
814 | */ | ||
815 | list_for_each_entry(state, &sp->so_states, open_states) { | ||
816 | if (state->state == 0) | ||
817 | continue; | ||
818 | status = ops->recover_open(sp, state); | ||
819 | list_for_each_entry(lock, &state->lock_states, ls_locks) | ||
820 | lock->ls_flags &= ~NFS_LOCK_INITIALIZED; | ||
821 | if (status >= 0) { | ||
822 | status = nfs4_reclaim_locks(ops, state); | ||
823 | if (status < 0) | ||
824 | goto out_err; | ||
825 | list_for_each_entry(lock, &state->lock_states, ls_locks) { | ||
826 | if (!(lock->ls_flags & NFS_LOCK_INITIALIZED)) | ||
827 | printk("%s: Lock reclaim failed!\n", | ||
828 | __FUNCTION__); | ||
829 | } | ||
830 | continue; | ||
831 | } | ||
832 | switch (status) { | ||
833 | default: | ||
834 | printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", | ||
835 | __FUNCTION__, status); | ||
836 | case -ENOENT: | ||
837 | case -NFS4ERR_RECLAIM_BAD: | ||
838 | case -NFS4ERR_RECLAIM_CONFLICT: | ||
839 | /* | ||
840 | * Open state on this file cannot be recovered | ||
841 | * All we can do is revert to using the zero stateid. | ||
842 | */ | ||
843 | memset(state->stateid.data, 0, | ||
844 | sizeof(state->stateid.data)); | ||
845 | /* Mark the file as being 'closed' */ | ||
846 | state->state = 0; | ||
847 | break; | ||
848 | case -NFS4ERR_EXPIRED: | ||
849 | case -NFS4ERR_NO_GRACE: | ||
850 | case -NFS4ERR_STALE_CLIENTID: | ||
851 | goto out_err; | ||
852 | } | ||
853 | } | ||
854 | return 0; | ||
855 | out_err: | ||
856 | return status; | ||
857 | } | ||
858 | |||
859 | static int reclaimer(void *ptr) | ||
860 | { | ||
861 | struct reclaimer_args *args = (struct reclaimer_args *)ptr; | ||
862 | struct nfs4_client *clp = args->clp; | ||
863 | struct nfs4_state_owner *sp; | ||
864 | struct nfs4_state_recovery_ops *ops; | ||
865 | int status = 0; | ||
866 | |||
867 | daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr)); | ||
868 | allow_signal(SIGKILL); | ||
869 | |||
870 | atomic_inc(&clp->cl_count); | ||
871 | complete(&args->complete); | ||
872 | |||
873 | /* Ensure exclusive access to NFSv4 state */ | ||
874 | lock_kernel(); | ||
875 | down_write(&clp->cl_sem); | ||
876 | /* Are there any NFS mounts out there? */ | ||
877 | if (list_empty(&clp->cl_superblocks)) | ||
878 | goto out; | ||
879 | restart_loop: | ||
880 | status = nfs4_proc_renew(clp); | ||
881 | switch (status) { | ||
882 | case 0: | ||
883 | case -NFS4ERR_CB_PATH_DOWN: | ||
884 | goto out; | ||
885 | case -NFS4ERR_STALE_CLIENTID: | ||
886 | case -NFS4ERR_LEASE_MOVED: | ||
887 | ops = &nfs4_reboot_recovery_ops; | ||
888 | break; | ||
889 | default: | ||
890 | ops = &nfs4_network_partition_recovery_ops; | ||
891 | }; | ||
892 | status = __nfs4_init_client(clp); | ||
893 | if (status) | ||
894 | goto out_error; | ||
895 | /* Mark all delegations for reclaim */ | ||
896 | nfs_delegation_mark_reclaim(clp); | ||
897 | /* Note: list is protected by exclusive lock on cl->cl_sem */ | ||
898 | list_for_each_entry(sp, &clp->cl_state_owners, so_list) { | ||
899 | status = nfs4_reclaim_open_state(ops, sp); | ||
900 | if (status < 0) { | ||
901 | if (status == -NFS4ERR_NO_GRACE) { | ||
902 | ops = &nfs4_network_partition_recovery_ops; | ||
903 | status = nfs4_reclaim_open_state(ops, sp); | ||
904 | } | ||
905 | if (status == -NFS4ERR_STALE_CLIENTID) | ||
906 | goto restart_loop; | ||
907 | if (status == -NFS4ERR_EXPIRED) | ||
908 | goto restart_loop; | ||
909 | } | ||
910 | } | ||
911 | nfs_delegation_reap_unclaimed(clp); | ||
912 | out: | ||
913 | set_bit(NFS4CLNT_OK, &clp->cl_state); | ||
914 | up_write(&clp->cl_sem); | ||
915 | unlock_kernel(); | ||
916 | wake_up_all(&clp->cl_waitq); | ||
917 | rpc_wake_up(&clp->cl_rpcwaitq); | ||
918 | if (status == -NFS4ERR_CB_PATH_DOWN) | ||
919 | nfs_handle_cb_pathdown(clp); | ||
920 | nfs4_put_client(clp); | ||
921 | return 0; | ||
922 | out_error: | ||
923 | printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n", | ||
924 | NIPQUAD(clp->cl_addr.s_addr), -status); | ||
925 | goto out; | ||
926 | } | ||
927 | |||
928 | /* | ||
929 | * Local variables: | ||
930 | * c-basic-offset: 8 | ||
931 | * End: | ||
932 | */ | ||