blob: 9cfe6864d9ed506da17241828dd7c1411af57e23 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Trond Myklebust6f43ddc2007-07-08 16:49:11 -040041#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/slab.h>
43#include <linux/smp_lock.h>
44#include <linux/nfs_fs.h>
45#include <linux/nfs_idmap.h>
Trond Myklebust5043e902006-01-03 09:55:23 +010046#include <linux/kthread.h>
47#include <linux/module.h>
Trond Myklebust9f958ab2007-07-02 13:58:33 -040048#include <linux/random.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <linux/workqueue.h>
50#include <linux/bitops.h>
51
Trond Myklebust4ce79712005-06-22 17:16:21 +000052#include "nfs4_fs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#include "callback.h"
54#include "delegation.h"
David Howells24c8dbb2006-08-22 20:06:10 -040055#include "internal.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57#define OPENOWNER_POOL_SIZE 8
58
Trond Myklebust4ce79712005-06-22 17:16:21 +000059const nfs4_stateid zero_stateid;
60
Linus Torvalds1da177e2005-04-16 15:20:36 -070061static LIST_HEAD(nfs4_clientid_list);
62
Andy Adamson591d71c2009-04-01 09:22:47 -040063int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
Linus Torvalds1da177e2005-04-16 15:20:36 -070064{
Chuck Leverf738f512009-03-18 20:48:06 -040065 unsigned short port;
66 int status;
67
68 port = nfs_callback_tcpport;
69 if (clp->cl_addr.ss_family == AF_INET6)
70 port = nfs_callback_tcpport6;
71
72 status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 if (status == 0)
Trond Myklebust286d7d62006-01-03 09:55:26 +010074 status = nfs4_proc_setclientid_confirm(clp, cred);
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 if (status == 0)
76 nfs4_schedule_state_renewal(clp);
77 return status;
78}
79
Andy Adamsona7b72102009-04-01 09:22:46 -040080struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
Trond Myklebusta2b2bb82008-04-08 16:02:17 -040081{
82 struct rpc_cred *cred = NULL;
83
Trond Myklebusta2b2bb82008-04-08 16:02:17 -040084 if (clp->cl_machine_cred != NULL)
85 cred = get_rpccred(clp->cl_machine_cred);
Trond Myklebusta2b2bb82008-04-08 16:02:17 -040086 return cred;
87}
88
89static void nfs4_clear_machine_cred(struct nfs_client *clp)
90{
91 struct rpc_cred *cred;
92
93 spin_lock(&clp->cl_lock);
94 cred = clp->cl_machine_cred;
95 clp->cl_machine_cred = NULL;
96 spin_unlock(&clp->cl_lock);
97 if (cred != NULL)
98 put_rpccred(cred);
99}
100
Trond Myklebust6dc9d572008-12-23 15:21:41 -0500101struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp)
Trond Myklebustb4454fe2006-01-03 09:55:25 +0100102{
103 struct nfs4_state_owner *sp;
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400104 struct rb_node *pos;
Trond Myklebustb4454fe2006-01-03 09:55:25 +0100105 struct rpc_cred *cred = NULL;
106
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400107 for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
108 sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
Trond Myklebustb4454fe2006-01-03 09:55:25 +0100109 if (list_empty(&sp->so_states))
110 continue;
111 cred = get_rpccred(sp->so_cred);
112 break;
113 }
114 return cred;
115}
116
Andy Adamsonb4b82602009-04-01 09:22:49 -0400117#if defined(CONFIG_NFS_V4_1)
118
Ricardo Labiaga9430fb62009-12-06 12:23:46 -0500119static int nfs41_setup_state_renewal(struct nfs_client *clp)
120{
121 int status;
122 struct nfs_fsinfo fsinfo;
123
124 status = nfs4_proc_get_lease_time(clp, &fsinfo);
125 if (status == 0) {
126 /* Update lease time and schedule renewal */
127 spin_lock(&clp->cl_lock);
128 clp->cl_lease_time = fsinfo.lease_time * HZ;
129 clp->cl_last_renewal = jiffies;
130 spin_unlock(&clp->cl_lock);
131
132 nfs4_schedule_state_renewal(clp);
133 }
134
135 return status;
136}
137
Andy Adamson4d643d12009-12-04 15:52:24 -0500138int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
139{
140 int status;
141
142 status = nfs4_proc_exchange_id(clp, cred);
Ricardo Labiaga9430fb62009-12-06 12:23:46 -0500143 if (status != 0)
144 goto out;
145 status = nfs4_proc_create_session(clp);
146 if (status != 0)
147 goto out;
148 nfs41_setup_state_renewal(clp);
149 nfs_mark_client_ready(clp, NFS_CS_READY);
150out:
Andy Adamson4d643d12009-12-04 15:52:24 -0500151 return status;
152}
153
Andy Adamsonb4b82602009-04-01 09:22:49 -0400154struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp)
155{
156 struct rpc_cred *cred;
157
158 spin_lock(&clp->cl_lock);
159 cred = nfs4_get_machine_cred_locked(clp);
160 spin_unlock(&clp->cl_lock);
161 return cred;
162}
163
164#endif /* CONFIG_NFS_V4_1 */
165
Andy Adamsona7b72102009-04-01 09:22:46 -0400166struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
Trond Myklebust286d7d62006-01-03 09:55:26 +0100167{
168 struct nfs4_state_owner *sp;
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400169 struct rb_node *pos;
Trond Myklebusta2b2bb82008-04-08 16:02:17 -0400170 struct rpc_cred *cred;
Trond Myklebust286d7d62006-01-03 09:55:26 +0100171
Trond Myklebust6dc9d572008-12-23 15:21:41 -0500172 spin_lock(&clp->cl_lock);
173 cred = nfs4_get_machine_cred_locked(clp);
Trond Myklebusta2b2bb82008-04-08 16:02:17 -0400174 if (cred != NULL)
175 goto out;
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400176 pos = rb_first(&clp->cl_state_owners);
177 if (pos != NULL) {
178 sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
Trond Myklebusta2b2bb82008-04-08 16:02:17 -0400179 cred = get_rpccred(sp->so_cred);
Trond Myklebust286d7d62006-01-03 09:55:26 +0100180 }
Trond Myklebusta2b2bb82008-04-08 16:02:17 -0400181out:
Trond Myklebust6dc9d572008-12-23 15:21:41 -0500182 spin_unlock(&clp->cl_lock);
Trond Myklebusta2b2bb82008-04-08 16:02:17 -0400183 return cred;
Trond Myklebust286d7d62006-01-03 09:55:26 +0100184}
185
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400186static void nfs_alloc_unique_id(struct rb_root *root, struct nfs_unique_id *new,
187 __u64 minval, int maxbits)
188{
189 struct rb_node **p, *parent;
190 struct nfs_unique_id *pos;
191 __u64 mask = ~0ULL;
192
193 if (maxbits < 64)
194 mask = (1ULL << maxbits) - 1ULL;
195
196 /* Ensure distribution is more or less flat */
197 get_random_bytes(&new->id, sizeof(new->id));
198 new->id &= mask;
199 if (new->id < minval)
200 new->id += minval;
201retry:
202 p = &root->rb_node;
203 parent = NULL;
204
205 while (*p != NULL) {
206 parent = *p;
207 pos = rb_entry(parent, struct nfs_unique_id, rb_node);
208
209 if (new->id < pos->id)
210 p = &(*p)->rb_left;
211 else if (new->id > pos->id)
212 p = &(*p)->rb_right;
213 else
214 goto id_exists;
215 }
216 rb_link_node(&new->rb_node, parent, p);
217 rb_insert_color(&new->rb_node, root);
218 return;
219id_exists:
220 for (;;) {
221 new->id++;
222 if (new->id < minval || (new->id & mask) != new->id) {
223 new->id = minval;
224 break;
225 }
226 parent = rb_next(parent);
227 if (parent == NULL)
228 break;
229 pos = rb_entry(parent, struct nfs_unique_id, rb_node);
230 if (new->id < pos->id)
231 break;
232 }
233 goto retry;
234}
235
236static void nfs_free_unique_id(struct rb_root *root, struct nfs_unique_id *id)
237{
238 rb_erase(&id->rb_node, root);
239}
240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241static struct nfs4_state_owner *
Trond Myklebust6f2e64d2007-07-06 10:53:21 -0400242nfs4_find_state_owner(struct nfs_server *server, struct rpc_cred *cred)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243{
Trond Myklebust6f2e64d2007-07-06 10:53:21 -0400244 struct nfs_client *clp = server->nfs_client;
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400245 struct rb_node **p = &clp->cl_state_owners.rb_node,
246 *parent = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 struct nfs4_state_owner *sp, *res = NULL;
248
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400249 while (*p != NULL) {
250 parent = *p;
251 sp = rb_entry(parent, struct nfs4_state_owner, so_client_node);
252
Trond Myklebust6f2e64d2007-07-06 10:53:21 -0400253 if (server < sp->so_server) {
254 p = &parent->rb_left;
255 continue;
256 }
257 if (server > sp->so_server) {
258 p = &parent->rb_right;
259 continue;
260 }
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400261 if (cred < sp->so_cred)
262 p = &parent->rb_left;
263 else if (cred > sp->so_cred)
264 p = &parent->rb_right;
265 else {
266 atomic_inc(&sp->so_count);
267 res = sp;
268 break;
269 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 }
271 return res;
272}
273
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400274static struct nfs4_state_owner *
275nfs4_insert_state_owner(struct nfs_client *clp, struct nfs4_state_owner *new)
276{
277 struct rb_node **p = &clp->cl_state_owners.rb_node,
278 *parent = NULL;
279 struct nfs4_state_owner *sp;
280
281 while (*p != NULL) {
282 parent = *p;
283 sp = rb_entry(parent, struct nfs4_state_owner, so_client_node);
284
Trond Myklebust6f2e64d2007-07-06 10:53:21 -0400285 if (new->so_server < sp->so_server) {
286 p = &parent->rb_left;
287 continue;
288 }
289 if (new->so_server > sp->so_server) {
290 p = &parent->rb_right;
291 continue;
292 }
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400293 if (new->so_cred < sp->so_cred)
294 p = &parent->rb_left;
295 else if (new->so_cred > sp->so_cred)
296 p = &parent->rb_right;
297 else {
298 atomic_inc(&sp->so_count);
299 return sp;
300 }
301 }
302 nfs_alloc_unique_id(&clp->cl_openowner_id, &new->so_owner_id, 1, 64);
303 rb_link_node(&new->so_client_node, parent, p);
304 rb_insert_color(&new->so_client_node, &clp->cl_state_owners);
305 return new;
306}
307
308static void
309nfs4_remove_state_owner(struct nfs_client *clp, struct nfs4_state_owner *sp)
310{
311 if (!RB_EMPTY_NODE(&sp->so_client_node))
312 rb_erase(&sp->so_client_node, &clp->cl_state_owners);
313 nfs_free_unique_id(&clp->cl_openowner_id, &sp->so_owner_id);
314}
315
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316/*
317 * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to
318 * create a new state_owner.
319 *
320 */
321static struct nfs4_state_owner *
322nfs4_alloc_state_owner(void)
323{
324 struct nfs4_state_owner *sp;
325
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700326 sp = kzalloc(sizeof(*sp),GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 if (!sp)
328 return NULL;
Trond Myklebustec073422005-10-20 14:22:47 -0700329 spin_lock_init(&sp->so_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 INIT_LIST_HEAD(&sp->so_states);
331 INIT_LIST_HEAD(&sp->so_delegations);
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700332 rpc_init_wait_queue(&sp->so_sequence.wait, "Seqid_waitqueue");
333 sp->so_seqid.sequence = &sp->so_sequence;
334 spin_lock_init(&sp->so_sequence.lock);
335 INIT_LIST_HEAD(&sp->so_sequence.list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 atomic_set(&sp->so_count, 1);
337 return sp;
338}
339
Adrian Bunk1d2e88e2008-05-02 13:42:45 -0700340static void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341nfs4_drop_state_owner(struct nfs4_state_owner *sp)
342{
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400343 if (!RB_EMPTY_NODE(&sp->so_client_node)) {
344 struct nfs_client *clp = sp->so_client;
345
346 spin_lock(&clp->cl_lock);
347 rb_erase(&sp->so_client_node, &clp->cl_state_owners);
348 RB_CLEAR_NODE(&sp->so_client_node);
349 spin_unlock(&clp->cl_lock);
350 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351}
352
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred)
354{
David Howells7539bba2006-08-22 20:06:09 -0400355 struct nfs_client *clp = server->nfs_client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 struct nfs4_state_owner *sp, *new;
357
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 spin_lock(&clp->cl_lock);
Trond Myklebust6f2e64d2007-07-06 10:53:21 -0400359 sp = nfs4_find_state_owner(server, cred);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 spin_unlock(&clp->cl_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 if (sp != NULL)
362 return sp;
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400363 new = nfs4_alloc_state_owner();
364 if (new == NULL)
365 return NULL;
366 new->so_client = clp;
Trond Myklebust6f2e64d2007-07-06 10:53:21 -0400367 new->so_server = server;
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400368 new->so_cred = cred;
369 spin_lock(&clp->cl_lock);
370 sp = nfs4_insert_state_owner(clp, new);
371 spin_unlock(&clp->cl_lock);
372 if (sp == new)
373 get_rpccred(cred);
Trond Myklebustf6a1cc82008-02-22 17:06:55 -0500374 else {
375 rpc_destroy_wait_queue(&new->so_sequence.wait);
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400376 kfree(new);
Trond Myklebustf6a1cc82008-02-22 17:06:55 -0500377 }
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400378 return sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379}
380
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381void nfs4_put_state_owner(struct nfs4_state_owner *sp)
382{
David Howellsadfa6f92006-08-22 20:06:08 -0400383 struct nfs_client *clp = sp->so_client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 struct rpc_cred *cred = sp->so_cred;
385
386 if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock))
387 return;
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400388 nfs4_remove_state_owner(clp, sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 spin_unlock(&clp->cl_lock);
Trond Myklebustf6a1cc82008-02-22 17:06:55 -0500390 rpc_destroy_wait_queue(&sp->so_sequence.wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 put_rpccred(cred);
392 kfree(sp);
393}
394
395static struct nfs4_state *
396nfs4_alloc_open_state(void)
397{
398 struct nfs4_state *state;
399
Trond Myklebuste7616922006-01-03 09:55:13 +0100400 state = kzalloc(sizeof(*state), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 if (!state)
402 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 atomic_set(&state->count, 1);
404 INIT_LIST_HEAD(&state->lock_states);
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000405 spin_lock_init(&state->state_lock);
Trond Myklebust8bda4e42007-07-09 10:45:42 -0400406 seqlock_init(&state->seqlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 return state;
408}
409
Trond Myklebust4cecb762005-11-04 15:32:58 -0500410void
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500411nfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode)
Trond Myklebust4cecb762005-11-04 15:32:58 -0500412{
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500413 if (state->state == fmode)
Trond Myklebust4cecb762005-11-04 15:32:58 -0500414 return;
415 /* NB! List reordering - see the reclaim code for why. */
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500416 if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
417 if (fmode & FMODE_WRITE)
Trond Myklebust4cecb762005-11-04 15:32:58 -0500418 list_move(&state->open_states, &state->owner->so_states);
419 else
420 list_move_tail(&state->open_states, &state->owner->so_states);
421 }
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500422 state->state = fmode;
Trond Myklebust4cecb762005-11-04 15:32:58 -0500423}
424
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425static struct nfs4_state *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426__nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
427{
428 struct nfs_inode *nfsi = NFS_I(inode);
429 struct nfs4_state *state;
430
431 list_for_each_entry(state, &nfsi->open_states, inode_states) {
Trond Myklebust1c816ef2007-07-03 14:41:19 -0400432 if (state->owner != owner)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 continue;
Trond Myklebust1c816ef2007-07-03 14:41:19 -0400434 if (atomic_inc_not_zero(&state->count))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 return state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 }
437 return NULL;
438}
439
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440static void
441nfs4_free_open_state(struct nfs4_state *state)
442{
443 kfree(state);
444}
445
446struct nfs4_state *
447nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
448{
449 struct nfs4_state *state, *new;
450 struct nfs_inode *nfsi = NFS_I(inode);
451
452 spin_lock(&inode->i_lock);
453 state = __nfs4_find_state_byowner(inode, owner);
454 spin_unlock(&inode->i_lock);
455 if (state)
456 goto out;
457 new = nfs4_alloc_open_state();
Trond Myklebustec073422005-10-20 14:22:47 -0700458 spin_lock(&owner->so_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 spin_lock(&inode->i_lock);
460 state = __nfs4_find_state_byowner(inode, owner);
461 if (state == NULL && new != NULL) {
462 state = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 state->owner = owner;
464 atomic_inc(&owner->so_count);
465 list_add(&state->inode_states, &nfsi->open_states);
466 state->inode = igrab(inode);
467 spin_unlock(&inode->i_lock);
Trond Myklebustec073422005-10-20 14:22:47 -0700468 /* Note: The reclaim code dictates that we add stateless
469 * and read-only stateids to the end of the list */
470 list_add_tail(&state->open_states, &owner->so_states);
471 spin_unlock(&owner->so_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 } else {
473 spin_unlock(&inode->i_lock);
Trond Myklebustec073422005-10-20 14:22:47 -0700474 spin_unlock(&owner->so_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 if (new)
476 nfs4_free_open_state(new);
477 }
478out:
479 return state;
480}
481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482void nfs4_put_open_state(struct nfs4_state *state)
483{
484 struct inode *inode = state->inode;
485 struct nfs4_state_owner *owner = state->owner;
486
Trond Myklebustec073422005-10-20 14:22:47 -0700487 if (!atomic_dec_and_lock(&state->count, &owner->so_lock))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 return;
Trond Myklebustec073422005-10-20 14:22:47 -0700489 spin_lock(&inode->i_lock);
Trond Myklebustba6830312007-07-27 10:23:05 -0400490 list_del(&state->inode_states);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 list_del(&state->open_states);
Trond Myklebustec073422005-10-20 14:22:47 -0700492 spin_unlock(&inode->i_lock);
493 spin_unlock(&owner->so_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 iput(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 nfs4_free_open_state(state);
496 nfs4_put_state_owner(owner);
497}
498
499/*
Trond Myklebust83c9d412005-10-18 14:20:13 -0700500 * Close the current file.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 */
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500502static void __nfs4_close(struct path *path, struct nfs4_state *state, fmode_t fmode, int wait)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 struct nfs4_state_owner *owner = state->owner;
Trond Myklebust003707c2007-07-05 18:07:55 -0400505 int call_close = 0;
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500506 fmode_t newstate;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508 atomic_inc(&owner->so_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 /* Protect against nfs4_find_state() */
Trond Myklebustec073422005-10-20 14:22:47 -0700510 spin_lock(&owner->so_lock);
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500511 switch (fmode & (FMODE_READ | FMODE_WRITE)) {
Trond Myklebuste7616922006-01-03 09:55:13 +0100512 case FMODE_READ:
513 state->n_rdonly--;
514 break;
515 case FMODE_WRITE:
516 state->n_wronly--;
517 break;
518 case FMODE_READ|FMODE_WRITE:
519 state->n_rdwr--;
520 }
Trond Myklebust003707c2007-07-05 18:07:55 -0400521 newstate = FMODE_READ|FMODE_WRITE;
Trond Myklebuste7616922006-01-03 09:55:13 +0100522 if (state->n_rdwr == 0) {
Trond Myklebust003707c2007-07-05 18:07:55 -0400523 if (state->n_rdonly == 0) {
Trond Myklebuste7616922006-01-03 09:55:13 +0100524 newstate &= ~FMODE_READ;
Trond Myklebust003707c2007-07-05 18:07:55 -0400525 call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags);
526 call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
527 }
528 if (state->n_wronly == 0) {
Trond Myklebuste7616922006-01-03 09:55:13 +0100529 newstate &= ~FMODE_WRITE;
Trond Myklebust003707c2007-07-05 18:07:55 -0400530 call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags);
531 call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
532 }
533 if (newstate == 0)
534 clear_bit(NFS_DELEGATED_STATE, &state->flags);
Trond Myklebuste7616922006-01-03 09:55:13 +0100535 }
Trond Myklebust003707c2007-07-05 18:07:55 -0400536 nfs4_state_set_mode_locked(state, newstate);
Trond Myklebustec073422005-10-20 14:22:47 -0700537 spin_unlock(&owner->so_lock);
Trond Myklebust4cecb762005-11-04 15:32:58 -0500538
Trond Myklebust003707c2007-07-05 18:07:55 -0400539 if (!call_close) {
Trond Myklebustb39e6252007-06-11 23:05:07 -0400540 nfs4_put_open_state(state);
541 nfs4_put_state_owner(owner);
542 } else
Trond Myklebusta49c3c72007-10-18 18:03:27 -0400543 nfs4_do_close(path, state, wait);
544}
545
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500546void nfs4_close_state(struct path *path, struct nfs4_state *state, fmode_t fmode)
Trond Myklebusta49c3c72007-10-18 18:03:27 -0400547{
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500548 __nfs4_close(path, state, fmode, 0);
Trond Myklebusta49c3c72007-10-18 18:03:27 -0400549}
550
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500551void nfs4_close_sync(struct path *path, struct nfs4_state *state, fmode_t fmode)
Trond Myklebusta49c3c72007-10-18 18:03:27 -0400552{
Trond Myklebustdc0b0272008-12-23 15:21:56 -0500553 __nfs4_close(path, state, fmode, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554}
555
556/*
557 * Search the state->lock_states for an existing lock_owner
558 * that is compatible with current->files
559 */
560static struct nfs4_lock_state *
561__nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
562{
563 struct nfs4_lock_state *pos;
564 list_for_each_entry(pos, &state->lock_states, ls_locks) {
565 if (pos->ls_owner != fl_owner)
566 continue;
567 atomic_inc(&pos->ls_count);
568 return pos;
569 }
570 return NULL;
571}
572
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573/*
574 * Return a compatible lock_state. If no initialized lock_state structure
575 * exists, return an uninitialized one.
576 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 */
578static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
579{
580 struct nfs4_lock_state *lsp;
David Howellsadfa6f92006-08-22 20:06:08 -0400581 struct nfs_client *clp = state->owner->so_client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700583 lsp = kzalloc(sizeof(*lsp), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 if (lsp == NULL)
585 return NULL;
Trond Myklebustd0dc3702008-01-10 16:07:54 -0500586 rpc_init_wait_queue(&lsp->ls_sequence.wait, "lock_seqid_waitqueue");
587 spin_lock_init(&lsp->ls_sequence.lock);
588 INIT_LIST_HEAD(&lsp->ls_sequence.list);
589 lsp->ls_seqid.sequence = &lsp->ls_sequence;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 atomic_set(&lsp->ls_count, 1);
Trond Myklebustb64aec82009-07-21 16:47:46 -0400591 lsp->ls_state = state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 lsp->ls_owner = fl_owner;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 spin_lock(&clp->cl_lock);
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400594 nfs_alloc_unique_id(&clp->cl_lockowner_id, &lsp->ls_id, 1, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 spin_unlock(&clp->cl_lock);
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000596 INIT_LIST_HEAD(&lsp->ls_locks);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 return lsp;
598}
599
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400600static void nfs4_free_lock_state(struct nfs4_lock_state *lsp)
601{
602 struct nfs_client *clp = lsp->ls_state->owner->so_client;
603
604 spin_lock(&clp->cl_lock);
605 nfs_free_unique_id(&clp->cl_lockowner_id, &lsp->ls_id);
606 spin_unlock(&clp->cl_lock);
Trond Myklebustf6a1cc82008-02-22 17:06:55 -0500607 rpc_destroy_wait_queue(&lsp->ls_sequence.wait);
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400608 kfree(lsp);
609}
610
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611/*
612 * Return a compatible lock_state. If no initialized lock_state structure
613 * exists, return an uninitialized one.
614 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 */
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000616static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617{
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000618 struct nfs4_lock_state *lsp, *new = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000620 for(;;) {
621 spin_lock(&state->state_lock);
622 lsp = __nfs4_find_lock_state(state, owner);
623 if (lsp != NULL)
624 break;
625 if (new != NULL) {
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000626 list_add(&new->ls_locks, &state->lock_states);
627 set_bit(LK_STATE_IN_USE, &state->flags);
628 lsp = new;
629 new = NULL;
630 break;
631 }
632 spin_unlock(&state->state_lock);
633 new = nfs4_alloc_lock_state(state, owner);
634 if (new == NULL)
635 return NULL;
636 }
637 spin_unlock(&state->state_lock);
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400638 if (new != NULL)
639 nfs4_free_lock_state(new);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 return lsp;
641}
642
643/*
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000644 * Release reference to lock_state, and free it if we see that
645 * it is no longer in use
646 */
Trond Myklebustfaf5f492005-10-18 14:20:15 -0700647void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000648{
649 struct nfs4_state *state;
650
651 if (lsp == NULL)
652 return;
653 state = lsp->ls_state;
654 if (!atomic_dec_and_lock(&lsp->ls_count, &state->state_lock))
655 return;
656 list_del(&lsp->ls_locks);
657 if (list_empty(&state->lock_states))
658 clear_bit(LK_STATE_IN_USE, &state->flags);
659 spin_unlock(&state->state_lock);
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400660 nfs4_free_lock_state(lsp);
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000661}
662
663static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
664{
665 struct nfs4_lock_state *lsp = src->fl_u.nfs4_fl.owner;
666
667 dst->fl_u.nfs4_fl.owner = lsp;
668 atomic_inc(&lsp->ls_count);
669}
670
671static void nfs4_fl_release_lock(struct file_lock *fl)
672{
673 nfs4_put_lock_state(fl->fl_u.nfs4_fl.owner);
674}
675
Alexey Dobriyan6aed6282009-09-21 17:01:11 -0700676static const struct file_lock_operations nfs4_fl_lock_ops = {
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000677 .fl_copy_lock = nfs4_fl_copy_lock,
678 .fl_release_private = nfs4_fl_release_lock,
679};
680
681int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl)
682{
683 struct nfs4_lock_state *lsp;
684
685 if (fl->fl_ops != NULL)
686 return 0;
687 lsp = nfs4_get_lock_state(state, fl->fl_owner);
688 if (lsp == NULL)
689 return -ENOMEM;
690 fl->fl_u.nfs4_fl.owner = lsp;
691 fl->fl_ops = &nfs4_fl_lock_ops;
692 return 0;
693}
694
695/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 * Byte-range lock aware utility to initialize the stateid of read/write
697 * requests.
698 */
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000699void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700{
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000701 struct nfs4_lock_state *lsp;
Trond Myklebust8bda4e42007-07-09 10:45:42 -0400702 int seq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
Trond Myklebust8bda4e42007-07-09 10:45:42 -0400704 do {
705 seq = read_seqbegin(&state->seqlock);
706 memcpy(dst, &state->stateid, sizeof(*dst));
707 } while (read_seqretry(&state->seqlock, seq));
Trond Myklebust8d0a8a92005-06-22 17:16:32 +0000708 if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
709 return;
710
711 spin_lock(&state->state_lock);
712 lsp = __nfs4_find_lock_state(state, fl_owner);
713 if (lsp != NULL && (lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0)
714 memcpy(dst, &lsp->ls_stateid, sizeof(*dst));
715 spin_unlock(&state->state_lock);
716 nfs4_put_lock_state(lsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717}
718
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700719struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720{
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700721 struct nfs_seqid *new;
722
723 new = kmalloc(sizeof(*new), GFP_KERNEL);
724 if (new != NULL) {
725 new->sequence = counter;
Trond Myklebust2f74c0a2008-01-08 17:56:07 -0500726 INIT_LIST_HEAD(&new->list);
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700727 }
728 return new;
729}
730
731void nfs_free_seqid(struct nfs_seqid *seqid)
732{
Trond Myklebust2f74c0a2008-01-08 17:56:07 -0500733 if (!list_empty(&seqid->list)) {
734 struct rpc_sequence *sequence = seqid->sequence->sequence;
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700735
Trond Myklebust2f74c0a2008-01-08 17:56:07 -0500736 spin_lock(&sequence->lock);
737 list_del(&seqid->list);
738 spin_unlock(&sequence->lock);
739 rpc_wake_up(&sequence->wait);
740 }
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700741 kfree(seqid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742}
743
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744/*
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700745 * Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
746 * failed with a seqid incrementing error -
747 * see comments nfs_fs.h:seqid_mutating_error()
748 */
Trond Myklebust88d90932007-07-02 14:03:03 -0400749static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750{
Trond Myklebust2f74c0a2008-01-08 17:56:07 -0500751 BUG_ON(list_first_entry(&seqid->sequence->sequence->list, struct nfs_seqid, list) != seqid);
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700752 switch (status) {
753 case 0:
754 break;
755 case -NFS4ERR_BAD_SEQID:
Trond Myklebust6f43ddc2007-07-08 16:49:11 -0400756 if (seqid->sequence->flags & NFS_SEQID_CONFIRMED)
757 return;
758 printk(KERN_WARNING "NFS: v4 server returned a bad"
Dan Muntz497799e2008-02-13 13:09:35 -0800759 " sequence-id error on an"
760 " unconfirmed sequence %p!\n",
Trond Myklebust6f43ddc2007-07-08 16:49:11 -0400761 seqid->sequence);
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700762 case -NFS4ERR_STALE_CLIENTID:
763 case -NFS4ERR_STALE_STATEID:
764 case -NFS4ERR_BAD_STATEID:
765 case -NFS4ERR_BADXDR:
766 case -NFS4ERR_RESOURCE:
767 case -NFS4ERR_NOFILEHANDLE:
768 /* Non-seqid mutating errors */
769 return;
770 };
771 /*
772 * Note: no locking needed as we are guaranteed to be first
773 * on the sequence list
774 */
775 seqid->sequence->counter++;
776}
777
778void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
779{
Benny Halevy34dc1ad2009-04-01 09:22:52 -0400780 struct nfs4_state_owner *sp = container_of(seqid->sequence,
781 struct nfs4_state_owner, so_seqid);
782 struct nfs_server *server = sp->so_server;
783
784 if (status == -NFS4ERR_BAD_SEQID)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 nfs4_drop_state_owner(sp);
Benny Halevy34dc1ad2009-04-01 09:22:52 -0400786 if (!nfs4_has_session(server->nfs_client))
787 nfs_increment_seqid(status, seqid);
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700788}
789
790/*
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700791 * Increment the seqid if the LOCK/LOCKU succeeded, or
792 * failed with a seqid incrementing error -
793 * see comments nfs_fs.h:seqid_mutating_error()
794 */
795void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid)
796{
Trond Myklebust88d90932007-07-02 14:03:03 -0400797 nfs_increment_seqid(status, seqid);
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700798}
799
800int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task)
801{
802 struct rpc_sequence *sequence = seqid->sequence->sequence;
803 int status = 0;
804
805 spin_lock(&sequence->lock);
Trond Myklebust2f74c0a2008-01-08 17:56:07 -0500806 if (list_empty(&seqid->list))
807 list_add_tail(&seqid->list, &sequence->list);
808 if (list_first_entry(&sequence->list, struct nfs_seqid, list) == seqid)
809 goto unlock;
Trond Myklebust5d008372008-02-22 16:34:17 -0500810 rpc_sleep_on(&sequence->wait, task, NULL);
Trond Myklebust2f74c0a2008-01-08 17:56:07 -0500811 status = -EAGAIN;
812unlock:
Trond Myklebustcee54fc2005-10-18 14:20:12 -0700813 spin_unlock(&sequence->lock);
814 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815}
816
Trond Myklebuste005e802008-12-23 15:21:48 -0500817static int nfs4_run_state_manager(void *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Trond Myklebuste005e802008-12-23 15:21:48 -0500819static void nfs4_clear_state_manager_bit(struct nfs_client *clp)
Trond Myklebust433fbe42006-01-03 09:55:22 +0100820{
821 smp_mb__before_clear_bit();
Trond Myklebuste005e802008-12-23 15:21:48 -0500822 clear_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
Trond Myklebust433fbe42006-01-03 09:55:22 +0100823 smp_mb__after_clear_bit();
Trond Myklebuste005e802008-12-23 15:21:48 -0500824 wake_up_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING);
Trond Myklebust433fbe42006-01-03 09:55:22 +0100825 rpc_wake_up(&clp->cl_rpcwaitq);
826}
827
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828/*
Trond Myklebuste005e802008-12-23 15:21:48 -0500829 * Schedule the nfs_client asynchronous state management routine
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 */
Trond Myklebustb0d3ded2008-12-23 15:21:50 -0500831void nfs4_schedule_state_manager(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832{
Trond Myklebust5043e902006-01-03 09:55:23 +0100833 struct task_struct *task;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Trond Myklebuste005e802008-12-23 15:21:48 -0500835 if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
836 return;
Trond Myklebust5043e902006-01-03 09:55:23 +0100837 __module_get(THIS_MODULE);
838 atomic_inc(&clp->cl_count);
Trond Myklebuste005e802008-12-23 15:21:48 -0500839 task = kthread_run(nfs4_run_state_manager, clp, "%s-manager",
Chuck Lever5d8515c2007-12-10 14:57:16 -0500840 rpc_peeraddr2str(clp->cl_rpcclient,
841 RPC_DISPLAY_ADDR));
Trond Myklebust5043e902006-01-03 09:55:23 +0100842 if (!IS_ERR(task))
843 return;
Trond Myklebuste005e802008-12-23 15:21:48 -0500844 nfs4_clear_state_manager_bit(clp);
David Howells24c8dbb2006-08-22 20:06:10 -0400845 nfs_put_client(clp);
Trond Myklebust5043e902006-01-03 09:55:23 +0100846 module_put(THIS_MODULE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847}
848
849/*
850 * Schedule a state recovery attempt
851 */
David Howellsadfa6f92006-08-22 20:06:08 -0400852void nfs4_schedule_state_recovery(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853{
854 if (!clp)
855 return;
Trond Myklebuste598d842008-12-23 15:21:42 -0500856 if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
857 set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
Trond Myklebuste005e802008-12-23 15:21:48 -0500858 nfs4_schedule_state_manager(clp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859}
860
Trond Myklebustb79a4a12008-12-23 15:21:41 -0500861static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
862{
863
864 set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
865 /* Don't recover state that expired before the reboot */
866 if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) {
867 clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
868 return 0;
869 }
Trond Myklebust7eff03a2008-12-23 15:21:43 -0500870 set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags);
Trond Myklebustb79a4a12008-12-23 15:21:41 -0500871 set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
872 return 1;
873}
874
Trond Myklebust9e33bed2008-12-23 15:21:46 -0500875int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
Trond Myklebustb79a4a12008-12-23 15:21:41 -0500876{
877 set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
878 clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
Trond Myklebust7eff03a2008-12-23 15:21:43 -0500879 set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags);
Trond Myklebustb79a4a12008-12-23 15:21:41 -0500880 set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
881 return 1;
882}
883
Trond Myklebust02860012008-12-23 15:21:40 -0500884static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885{
886 struct inode *inode = state->inode;
Trond Myklebust19e03c52008-12-23 15:21:44 -0500887 struct nfs_inode *nfsi = NFS_I(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 struct file_lock *fl;
889 int status = 0;
890
Trond Myklebust3f09df72009-06-17 13:23:00 -0700891 if (inode->i_flock == NULL)
892 return 0;
893
894 /* Guard against delegation returns and new lock/unlock calls */
Trond Myklebust19e03c52008-12-23 15:21:44 -0500895 down_write(&nfsi->rwsem);
Trond Myklebust3f09df72009-06-17 13:23:00 -0700896 /* Protect inode->i_flock using the BKL */
897 lock_kernel();
Harvey Harrison90dc7d22008-02-20 13:03:05 -0800898 for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
Trond Myklebust43b2a332005-11-04 15:35:30 -0500899 if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 continue;
Trond Myklebustcd3758e2007-08-10 17:44:32 -0400901 if (nfs_file_open_context(fl->fl_file)->state != state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 continue;
Trond Myklebust3f09df72009-06-17 13:23:00 -0700903 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 status = ops->recover_lock(state, fl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 switch (status) {
Trond Myklebust965b5d62009-06-17 13:22:59 -0700906 case 0:
907 break;
908 case -ESTALE:
909 case -NFS4ERR_ADMIN_REVOKED:
910 case -NFS4ERR_STALE_STATEID:
911 case -NFS4ERR_BAD_STATEID:
912 case -NFS4ERR_EXPIRED:
913 case -NFS4ERR_NO_GRACE:
914 case -NFS4ERR_STALE_CLIENTID:
Trond Myklebust9c4c7612009-12-03 15:58:56 -0500915 case -NFS4ERR_BADSESSION:
916 case -NFS4ERR_BADSLOT:
917 case -NFS4ERR_BAD_HIGH_SLOT:
918 case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
Trond Myklebust965b5d62009-06-17 13:22:59 -0700919 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 default:
921 printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
Harvey Harrison3110ff82008-05-02 13:42:44 -0700922 __func__, status);
Trond Myklebust965b5d62009-06-17 13:22:59 -0700923 case -ENOMEM:
924 case -NFS4ERR_DENIED:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 case -NFS4ERR_RECLAIM_BAD:
926 case -NFS4ERR_RECLAIM_CONFLICT:
Trond Myklebust43b2a332005-11-04 15:35:30 -0500927 /* kill_proc(fl->fl_pid, SIGLOST, 1); */
Trond Myklebust965b5d62009-06-17 13:22:59 -0700928 status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 }
Trond Myklebust3f09df72009-06-17 13:23:00 -0700930 lock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 }
Trond Myklebust3f09df72009-06-17 13:23:00 -0700932 unlock_kernel();
Trond Myklebust965b5d62009-06-17 13:22:59 -0700933out:
Trond Myklebust19e03c52008-12-23 15:21:44 -0500934 up_write(&nfsi->rwsem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 return status;
936}
937
Trond Myklebust02860012008-12-23 15:21:40 -0500938static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs4_state_recovery_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939{
940 struct nfs4_state *state;
941 struct nfs4_lock_state *lock;
942 int status = 0;
943
944 /* Note: we rely on the sp->so_states list being ordered
945 * so that we always reclaim open(O_RDWR) and/or open(O_WRITE)
946 * states first.
947 * This is needed to ensure that the server won't give us any
948 * read delegations that we have to return if, say, we are
949 * recovering after a network partition or a reboot from a
950 * server that doesn't support a grace period.
951 */
Trond Myklebustfe1d8192008-12-23 15:21:43 -0500952restart:
953 spin_lock(&sp->so_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 list_for_each_entry(state, &sp->so_states, open_states) {
Trond Myklebustb79a4a12008-12-23 15:21:41 -0500955 if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
956 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 if (state->state == 0)
958 continue;
Trond Myklebustfe1d8192008-12-23 15:21:43 -0500959 atomic_inc(&state->count);
960 spin_unlock(&sp->so_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 status = ops->recover_open(sp, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 if (status >= 0) {
Trond Myklebust02860012008-12-23 15:21:40 -0500963 status = nfs4_reclaim_locks(state, ops);
964 if (status >= 0) {
965 list_for_each_entry(lock, &state->lock_states, ls_locks) {
966 if (!(lock->ls_flags & NFS_LOCK_INITIALIZED))
967 printk("%s: Lock reclaim failed!\n",
Harvey Harrison3110ff82008-05-02 13:42:44 -0700968 __func__);
Trond Myklebust02860012008-12-23 15:21:40 -0500969 }
Trond Myklebustfe1d8192008-12-23 15:21:43 -0500970 nfs4_put_open_state(state);
971 goto restart;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 }
974 switch (status) {
975 default:
976 printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
Harvey Harrison3110ff82008-05-02 13:42:44 -0700977 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 case -ENOENT:
Trond Myklebust965b5d62009-06-17 13:22:59 -0700979 case -ENOMEM:
Trond Myklebustb79a4a12008-12-23 15:21:41 -0500980 case -ESTALE:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 /*
982 * Open state on this file cannot be recovered
983 * All we can do is revert to using the zero stateid.
984 */
985 memset(state->stateid.data, 0,
986 sizeof(state->stateid.data));
987 /* Mark the file as being 'closed' */
988 state->state = 0;
989 break;
Trond Myklebust965b5d62009-06-17 13:22:59 -0700990 case -NFS4ERR_ADMIN_REVOKED:
991 case -NFS4ERR_STALE_STATEID:
992 case -NFS4ERR_BAD_STATEID:
Trond Myklebustb79a4a12008-12-23 15:21:41 -0500993 case -NFS4ERR_RECLAIM_BAD:
994 case -NFS4ERR_RECLAIM_CONFLICT:
995 nfs4_state_mark_reclaim_nograce(sp->so_client, state);
996 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 case -NFS4ERR_EXPIRED:
998 case -NFS4ERR_NO_GRACE:
Trond Myklebustb79a4a12008-12-23 15:21:41 -0500999 nfs4_state_mark_reclaim_nograce(sp->so_client, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 case -NFS4ERR_STALE_CLIENTID:
Trond Myklebust9c4c7612009-12-03 15:58:56 -05001001 case -NFS4ERR_BADSESSION:
1002 case -NFS4ERR_BADSLOT:
1003 case -NFS4ERR_BAD_HIGH_SLOT:
1004 case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 goto out_err;
1006 }
Trond Myklebustfe1d8192008-12-23 15:21:43 -05001007 nfs4_put_open_state(state);
1008 goto restart;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 }
Trond Myklebustfe1d8192008-12-23 15:21:43 -05001010 spin_unlock(&sp->so_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 return 0;
1012out_err:
Trond Myklebustfe1d8192008-12-23 15:21:43 -05001013 nfs4_put_open_state(state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 return status;
1015}
1016
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001017static void nfs4_clear_open_state(struct nfs4_state *state)
1018{
1019 struct nfs4_lock_state *lock;
1020
1021 clear_bit(NFS_DELEGATED_STATE, &state->flags);
1022 clear_bit(NFS_O_RDONLY_STATE, &state->flags);
1023 clear_bit(NFS_O_WRONLY_STATE, &state->flags);
1024 clear_bit(NFS_O_RDWR_STATE, &state->flags);
1025 list_for_each_entry(lock, &state->lock_states, ls_locks) {
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001026 lock->ls_seqid.flags = 0;
1027 lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
1028 }
1029}
1030
1031static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
Trond Myklebustcee54fc2005-10-18 14:20:12 -07001032{
1033 struct nfs4_state_owner *sp;
Trond Myklebust9f958ab2007-07-02 13:58:33 -04001034 struct rb_node *pos;
Trond Myklebustcee54fc2005-10-18 14:20:12 -07001035 struct nfs4_state *state;
Trond Myklebustcee54fc2005-10-18 14:20:12 -07001036
1037 /* Reset all sequence ids to zero */
Trond Myklebust9f958ab2007-07-02 13:58:33 -04001038 for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
1039 sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
Trond Myklebustcee54fc2005-10-18 14:20:12 -07001040 sp->so_seqid.flags = 0;
Trond Myklebustec073422005-10-20 14:22:47 -07001041 spin_lock(&sp->so_lock);
Trond Myklebustcee54fc2005-10-18 14:20:12 -07001042 list_for_each_entry(state, &sp->so_states, open_states) {
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001043 if (mark_reclaim(clp, state))
1044 nfs4_clear_open_state(state);
Trond Myklebustcee54fc2005-10-18 14:20:12 -07001045 }
Trond Myklebustec073422005-10-20 14:22:47 -07001046 spin_unlock(&sp->so_lock);
Trond Myklebustcee54fc2005-10-18 14:20:12 -07001047 }
1048}
1049
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001050static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp)
1051{
1052 /* Mark all delegations for reclaim */
1053 nfs_delegation_mark_reclaim(clp);
1054 nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot);
1055}
1056
Ricardo Labiagafce5c832009-12-05 16:08:41 -05001057static void nfs4_reclaim_complete(struct nfs_client *clp,
1058 const struct nfs4_state_recovery_ops *ops)
1059{
1060 /* Notify the server we're done reclaiming our state */
1061 if (ops->reclaim_complete)
1062 (void)ops->reclaim_complete(clp);
1063}
1064
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001065static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
1066{
1067 struct nfs4_state_owner *sp;
1068 struct rb_node *pos;
1069 struct nfs4_state *state;
1070
Ricardo Labiagada6ebfe2009-12-05 16:08:41 -05001071 nfs4_reclaim_complete(clp,
1072 nfs4_reboot_recovery_ops[clp->cl_minorversion]);
1073
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001074 if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
1075 return;
1076
1077 for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
1078 sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
1079 spin_lock(&sp->so_lock);
1080 list_for_each_entry(state, &sp->so_states, open_states) {
1081 if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags))
1082 continue;
1083 nfs4_state_mark_reclaim_nograce(clp, state);
1084 }
1085 spin_unlock(&sp->so_lock);
1086 }
1087
1088 nfs_delegation_reap_unclaimed(clp);
1089}
1090
1091static void nfs_delegation_clear_all(struct nfs_client *clp)
1092{
1093 nfs_delegation_mark_reclaim(clp);
1094 nfs_delegation_reap_unclaimed(clp);
1095}
1096
1097static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
1098{
1099 nfs_delegation_clear_all(clp);
1100 nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
1101}
1102
Trond Myklebust4f7cdf12009-12-03 15:53:20 -05001103static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
Trond Myklebuste598d842008-12-23 15:21:42 -05001104{
1105 switch (error) {
1106 case -NFS4ERR_CB_PATH_DOWN:
Trond Myklebust707fb4b2008-12-23 15:21:47 -05001107 nfs_handle_cb_pathdown(clp);
Trond Myklebust4f7cdf12009-12-03 15:53:20 -05001108 return 0;
Trond Myklebustc8b7ae3d2009-12-03 15:53:21 -05001109 case -NFS4ERR_NO_GRACE:
1110 nfs4_state_end_reclaim_reboot(clp);
1111 return 0;
Trond Myklebuste598d842008-12-23 15:21:42 -05001112 case -NFS4ERR_STALE_CLIENTID:
1113 case -NFS4ERR_LEASE_MOVED:
1114 set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
Trond Myklebuste345e882009-12-03 15:52:41 -05001115 nfs4_state_end_reclaim_reboot(clp);
Trond Myklebuste598d842008-12-23 15:21:42 -05001116 nfs4_state_start_reclaim_reboot(clp);
1117 break;
1118 case -NFS4ERR_EXPIRED:
1119 set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
1120 nfs4_state_start_reclaim_nograce(clp);
Andy Adamson8ba9bf82009-12-04 15:55:34 -05001121 break;
Andy Adamsonc3fad1b2009-04-01 09:22:39 -04001122 case -NFS4ERR_BADSESSION:
1123 case -NFS4ERR_BADSLOT:
1124 case -NFS4ERR_BAD_HIGH_SLOT:
1125 case -NFS4ERR_DEADSESSION:
1126 case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
1127 case -NFS4ERR_SEQ_FALSE_RETRY:
1128 case -NFS4ERR_SEQ_MISORDERED:
Andy Adamson6df08182009-12-04 15:55:05 -05001129 set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
Andy Adamson0b9e2d42009-12-04 16:02:14 -05001130 /* Zero session reset errors */
1131 return 0;
Trond Myklebuste598d842008-12-23 15:21:42 -05001132 }
Trond Myklebust4f7cdf12009-12-03 15:53:20 -05001133 return error;
Trond Myklebuste598d842008-12-23 15:21:42 -05001134}
1135
Trond Myklebust02860012008-12-23 15:21:40 -05001136static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137{
Trond Myklebust9f958ab2007-07-02 13:58:33 -04001138 struct rb_node *pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 int status = 0;
1140
Trond Myklebust7eff03a2008-12-23 15:21:43 -05001141restart:
1142 spin_lock(&clp->cl_lock);
Trond Myklebust02860012008-12-23 15:21:40 -05001143 for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
1144 struct nfs4_state_owner *sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
Trond Myklebust7eff03a2008-12-23 15:21:43 -05001145 if (!test_and_clear_bit(ops->owner_flag_bit, &sp->so_flags))
1146 continue;
1147 atomic_inc(&sp->so_count);
1148 spin_unlock(&clp->cl_lock);
Trond Myklebust02860012008-12-23 15:21:40 -05001149 status = nfs4_reclaim_open_state(sp, ops);
Trond Myklebust7eff03a2008-12-23 15:21:43 -05001150 if (status < 0) {
1151 set_bit(ops->owner_flag_bit, &sp->so_flags);
1152 nfs4_put_state_owner(sp);
Trond Myklebust4f7cdf12009-12-03 15:53:20 -05001153 return nfs4_recovery_handle_error(clp, status);
Trond Myklebust7eff03a2008-12-23 15:21:43 -05001154 }
1155 nfs4_put_state_owner(sp);
1156 goto restart;
Trond Myklebust02860012008-12-23 15:21:40 -05001157 }
Trond Myklebust7eff03a2008-12-23 15:21:43 -05001158 spin_unlock(&clp->cl_lock);
Trond Myklebust02860012008-12-23 15:21:40 -05001159 return status;
1160}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161
Trond Myklebust02860012008-12-23 15:21:40 -05001162static int nfs4_check_lease(struct nfs_client *clp)
1163{
1164 struct rpc_cred *cred;
Benny Halevy8e69514f2009-04-01 09:22:45 -04001165 struct nfs4_state_maintenance_ops *ops =
1166 nfs4_state_renewal_ops[clp->cl_minorversion];
Trond Myklebust02860012008-12-23 15:21:40 -05001167 int status = -NFS4ERR_EXPIRED;
1168
Trond Myklebust0f605b52008-12-23 15:21:42 -05001169 /* Is the client already known to have an expired lease? */
1170 if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
1171 return 0;
Andy Adamsona7b72102009-04-01 09:22:46 -04001172 spin_lock(&clp->cl_lock);
1173 cred = ops->get_state_renewal_cred_locked(clp);
1174 spin_unlock(&clp->cl_lock);
Trond Myklebust0f605b52008-12-23 15:21:42 -05001175 if (cred == NULL) {
1176 cred = nfs4_get_setclientid_cred(clp);
1177 if (cred == NULL)
1178 goto out;
Trond Myklebust02860012008-12-23 15:21:40 -05001179 }
Benny Halevy8e69514f2009-04-01 09:22:45 -04001180 status = ops->renew_lease(clp, cred);
Trond Myklebust0f605b52008-12-23 15:21:42 -05001181 put_rpccred(cred);
1182out:
Trond Myklebust4f7cdf12009-12-03 15:53:20 -05001183 return nfs4_recovery_handle_error(clp, status);
Trond Myklebust02860012008-12-23 15:21:40 -05001184}
1185
1186static int nfs4_reclaim_lease(struct nfs_client *clp)
1187{
1188 struct rpc_cred *cred;
Andy Adamson591d71c2009-04-01 09:22:47 -04001189 struct nfs4_state_recovery_ops *ops =
1190 nfs4_reboot_recovery_ops[clp->cl_minorversion];
Trond Myklebust02860012008-12-23 15:21:40 -05001191 int status = -ENOENT;
1192
Andy Adamson90a16612009-04-01 09:22:48 -04001193 cred = ops->get_clid_cred(clp);
Trond Myklebust02860012008-12-23 15:21:40 -05001194 if (cred != NULL) {
Andy Adamson591d71c2009-04-01 09:22:47 -04001195 status = ops->establish_clid(clp, cred);
Trond Myklebust02860012008-12-23 15:21:40 -05001196 put_rpccred(cred);
1197 /* Handle case where the user hasn't set up machine creds */
1198 if (status == -EACCES && cred == clp->cl_machine_cred) {
1199 nfs4_clear_machine_cred(clp);
1200 status = -EAGAIN;
1201 }
Benny Halevyc2e713d2009-04-01 09:21:51 -04001202 if (status == -NFS4ERR_MINOR_VERS_MISMATCH)
1203 status = -EPROTONOSUPPORT;
Trond Myklebust02860012008-12-23 15:21:40 -05001204 }
1205 return status;
1206}
1207
Andy Adamson76db6d92009-04-01 09:22:38 -04001208#ifdef CONFIG_NFS_V4_1
Alexandros Batsakis0629e372009-12-05 13:46:14 -05001209void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
1210{
1211 if (!flags)
1212 return;
1213 else if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED) {
1214 set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
1215 nfs4_state_start_reclaim_reboot(clp);
1216 nfs4_schedule_state_recovery(clp);
1217 } else if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED |
1218 SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED |
1219 SEQ4_STATUS_ADMIN_STATE_REVOKED |
1220 SEQ4_STATUS_RECALLABLE_STATE_REVOKED |
1221 SEQ4_STATUS_LEASE_MOVED)) {
1222 set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
1223 nfs4_state_start_reclaim_nograce(clp);
1224 nfs4_schedule_state_recovery(clp);
1225 } else if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
1226 SEQ4_STATUS_BACKCHANNEL_FAULT |
1227 SEQ4_STATUS_CB_PATH_DOWN_SESSION))
1228 nfs_expire_all_delegations(clp);
1229}
1230
Andy Adamsonc3fad1b2009-04-01 09:22:39 -04001231static void nfs4_session_recovery_handle_error(struct nfs_client *clp, int err)
1232{
1233 switch (err) {
1234 case -NFS4ERR_STALE_CLIENTID:
1235 set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
Andy Adamsonc3fad1b2009-04-01 09:22:39 -04001236 }
1237}
1238
1239static int nfs4_reset_session(struct nfs_client *clp)
1240{
Andy Adamsonea028ac2009-12-04 15:55:38 -05001241 struct nfs4_session *ses = clp->cl_session;
1242 struct nfs4_slot_table *tbl = &ses->fc_slot_table;
Andy Adamsonc3fad1b2009-04-01 09:22:39 -04001243 int status;
1244
Andy Adamsonea028ac2009-12-04 15:55:38 -05001245 spin_lock(&tbl->slot_tbl_lock);
Trond Myklebust0556d1a2009-12-05 15:03:20 -05001246 set_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state);
Andy Adamsonea028ac2009-12-04 15:55:38 -05001247 if (tbl->highest_used_slotid != -1) {
Trond Myklebust35dc1d72009-12-05 19:32:19 -05001248 INIT_COMPLETION(ses->complete);
Andy Adamsonea028ac2009-12-04 15:55:38 -05001249 spin_unlock(&tbl->slot_tbl_lock);
1250 status = wait_for_completion_interruptible(&ses->complete);
Andy Adamsonea028ac2009-12-04 15:55:38 -05001251 if (status) /* -ERESTARTSYS */
1252 goto out;
1253 } else {
1254 spin_unlock(&tbl->slot_tbl_lock);
1255 }
1256
Andy Adamsonc3fad1b2009-04-01 09:22:39 -04001257 status = nfs4_proc_destroy_session(clp->cl_session);
1258 if (status && status != -NFS4ERR_BADSESSION &&
1259 status != -NFS4ERR_DEADSESSION) {
1260 nfs4_session_recovery_handle_error(clp, status);
1261 goto out;
1262 }
1263
1264 memset(clp->cl_session->sess_id.data, 0, NFS4_MAX_SESSIONID_LEN);
Trond Myklebustf26468f2009-12-05 19:32:11 -05001265 status = nfs4_proc_create_session(clp);
Andy Adamsonc3fad1b2009-04-01 09:22:39 -04001266 if (status)
1267 nfs4_session_recovery_handle_error(clp, status);
1268 /* fall through*/
1269out:
1270 /* Wake up the next rpc task even on error */
Andy Adamson0b9e2d42009-12-04 16:02:14 -05001271 clear_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state);
Trond Myklebust35dc1d72009-12-05 19:32:19 -05001272 rpc_wake_up(&clp->cl_session->fc_slot_table.slot_tbl_waitq);
Ricardo Labiaga9430fb62009-12-06 12:23:46 -05001273 if (status == 0)
1274 nfs41_setup_state_renewal(clp);
Andy Adamsonc3fad1b2009-04-01 09:22:39 -04001275 return status;
1276}
Andy Adamson76db6d92009-04-01 09:22:38 -04001277
Andy Adamson76db6d92009-04-01 09:22:38 -04001278#else /* CONFIG_NFS_V4_1 */
Andy Adamsonc3fad1b2009-04-01 09:22:39 -04001279static int nfs4_reset_session(struct nfs_client *clp) { return 0; }
Andy Adamson76db6d92009-04-01 09:22:38 -04001280#endif /* CONFIG_NFS_V4_1 */
1281
Andy Adamson78722e92009-06-16 14:43:15 -04001282/* Set NFS4CLNT_LEASE_EXPIRED for all v4.0 errors and for recoverable errors
1283 * on EXCHANGE_ID for v4.1
1284 */
1285static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
1286{
1287 if (nfs4_has_session(clp)) {
1288 switch (status) {
1289 case -NFS4ERR_DELAY:
1290 case -NFS4ERR_CLID_INUSE:
1291 case -EAGAIN:
1292 break;
1293
1294 case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
1295 * in nfs4_exchange_id */
1296 default:
1297 return;
1298 }
1299 }
1300 set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
1301}
1302
Trond Myklebuste005e802008-12-23 15:21:48 -05001303static void nfs4_state_manager(struct nfs_client *clp)
Trond Myklebust02860012008-12-23 15:21:40 -05001304{
Trond Myklebust02860012008-12-23 15:21:40 -05001305 int status = 0;
1306
Trond Myklebust02860012008-12-23 15:21:40 -05001307 /* Ensure exclusive access to NFSv4 state */
Trond Myklebustf3c76492008-12-23 15:21:48 -05001308 for(;;) {
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001309 if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
1310 /* We're going to have to re-establish a clientid */
1311 status = nfs4_reclaim_lease(clp);
1312 if (status) {
Andy Adamson78722e92009-06-16 14:43:15 -04001313 nfs4_set_lease_expired(clp, status);
Trond Myklebustb6d408b2009-12-03 15:53:22 -05001314 if (test_bit(NFS4CLNT_LEASE_EXPIRED,
1315 &clp->cl_state))
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001316 continue;
Andy Adamson76db6d92009-04-01 09:22:38 -04001317 if (clp->cl_cons_state ==
1318 NFS_CS_SESSION_INITING)
1319 nfs_mark_client_ready(clp, status);
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001320 goto out_error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 }
Trond Myklebuste598d842008-12-23 15:21:42 -05001322 clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
1323 }
1324
1325 if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
1326 status = nfs4_check_lease(clp);
Trond Myklebustb6d408b2009-12-03 15:53:22 -05001327 if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
Trond Myklebuste598d842008-12-23 15:21:42 -05001328 continue;
Trond Myklebustb6d408b2009-12-03 15:53:22 -05001329 if (status < 0 && status != -NFS4ERR_CB_PATH_DOWN)
1330 goto out_error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 }
Trond Myklebustb6d408b2009-12-03 15:53:22 -05001332
Andy Adamsonc3fad1b2009-04-01 09:22:39 -04001333 /* Initialize or reset the session */
Andy Adamson6df08182009-12-04 15:55:05 -05001334 if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)
Trond Myklebust7111dc72009-08-24 19:21:29 -04001335 && nfs4_has_session(clp)) {
Andy Adamson4d643d12009-12-04 15:52:24 -05001336 status = nfs4_reset_session(clp);
Trond Myklebustb6d408b2009-12-03 15:53:22 -05001337 if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
1338 continue;
1339 if (status < 0)
Andy Adamson76db6d92009-04-01 09:22:38 -04001340 goto out_error;
Andy Adamson76db6d92009-04-01 09:22:38 -04001341 }
Trond Myklebustb6d408b2009-12-03 15:53:22 -05001342
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001343 /* First recover reboot state... */
Trond Myklebuste345e882009-12-03 15:52:41 -05001344 if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
Andy Adamson591d71c2009-04-01 09:22:47 -04001345 status = nfs4_do_reclaim(clp,
1346 nfs4_reboot_recovery_ops[clp->cl_minorversion]);
Trond Myklebustb6d408b2009-12-03 15:53:22 -05001347 if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
Andy Adamson6df08182009-12-04 15:55:05 -05001348 test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state))
Andy Adamsonc3fad1b2009-04-01 09:22:39 -04001349 continue;
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001350 nfs4_state_end_reclaim_reboot(clp);
Trond Myklebustb6d408b2009-12-03 15:53:22 -05001351 if (test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state))
1352 continue;
1353 if (status < 0)
1354 goto out_error;
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001355 }
1356
1357 /* Now recover expired state... */
1358 if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
Andy Adamson591d71c2009-04-01 09:22:47 -04001359 status = nfs4_do_reclaim(clp,
1360 nfs4_nograce_recovery_ops[clp->cl_minorversion]);
Trond Myklebustb6d408b2009-12-03 15:53:22 -05001361 if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
Andy Adamson6df08182009-12-04 15:55:05 -05001362 test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) ||
Trond Myklebustb6d408b2009-12-03 15:53:22 -05001363 test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
1364 continue;
1365 if (status < 0)
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001366 goto out_error;
Trond Myklebustb79a4a12008-12-23 15:21:41 -05001367 }
Trond Myklebust707fb4b2008-12-23 15:21:47 -05001368
1369 if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
1370 nfs_client_return_marked_delegations(clp);
1371 continue;
1372 }
Trond Myklebuste005e802008-12-23 15:21:48 -05001373
1374 nfs4_clear_state_manager_bit(clp);
Trond Myklebustf3c76492008-12-23 15:21:48 -05001375 /* Did we race with an attempt to give us more work? */
1376 if (clp->cl_state == 0)
1377 break;
1378 if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
1379 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 }
Trond Myklebuste005e802008-12-23 15:21:48 -05001381 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382out_error:
Trond Myklebuste005e802008-12-23 15:21:48 -05001383 printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s"
Chuck Lever5d8515c2007-12-10 14:57:16 -05001384 " with error %d\n", clp->cl_hostname, -status);
Trond Myklebuste005e802008-12-23 15:21:48 -05001385 nfs4_clear_state_manager_bit(clp);
1386}
1387
1388static int nfs4_run_state_manager(void *ptr)
1389{
1390 struct nfs_client *clp = ptr;
1391
1392 allow_signal(SIGKILL);
1393 nfs4_state_manager(clp);
1394 nfs_put_client(clp);
1395 module_put_and_exit(0);
1396 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397}
1398
1399/*
1400 * Local variables:
1401 * c-basic-offset: 8
1402 * End:
1403 */