blob: 6dd48a4405b46d409af629d4195b27b17e83dab1 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/nfs/delegation.c
3 *
4 * Copyright (C) 2004 Trond Myklebust
5 *
6 * NFS file delegation management
7 *
8 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/completion.h>
Trond Myklebust58d97142006-01-03 09:55:24 +010010#include <linux/kthread.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/module.h>
12#include <linux/sched.h>
Alexey Dobriyan405f5572009-07-11 22:08:37 +040013#include <linux/smp_lock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/spinlock.h>
15
16#include <linux/nfs4.h>
17#include <linux/nfs_fs.h>
18#include <linux/nfs_xdr.h>
19
Trond Myklebust4ce79712005-06-22 17:16:21 +000020#include "nfs4_fs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include "delegation.h"
David Howells24c8dbb2006-08-22 20:06:10 -040022#include "internal.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
Trond Myklebust905f8d12007-08-06 12:18:34 -040024static void nfs_do_free_delegation(struct nfs_delegation *delegation)
Linus Torvalds1da177e2005-04-16 15:20:36 -070025{
Linus Torvalds1da177e2005-04-16 15:20:36 -070026 kfree(delegation);
27}
28
Trond Myklebust8383e462007-07-06 15:12:04 -040029static void nfs_free_delegation_callback(struct rcu_head *head)
30{
31 struct nfs_delegation *delegation = container_of(head, struct nfs_delegation, rcu);
32
Trond Myklebust905f8d12007-08-06 12:18:34 -040033 nfs_do_free_delegation(delegation);
34}
35
36static void nfs_free_delegation(struct nfs_delegation *delegation)
37{
38 struct rpc_cred *cred;
39
40 cred = rcu_dereference(delegation->cred);
41 rcu_assign_pointer(delegation->cred, NULL);
42 call_rcu(&delegation->rcu, nfs_free_delegation_callback);
43 if (cred)
44 put_rpccred(cred);
Trond Myklebust8383e462007-07-06 15:12:04 -040045}
46
Trond Myklebustb7391f42008-12-23 15:21:52 -050047void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
48{
49 set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
50}
51
Trond Myklebustbd7bf9d2008-12-23 15:21:53 -050052int nfs_have_delegation(struct inode *inode, fmode_t flags)
Trond Myklebustb7391f42008-12-23 15:21:52 -050053{
54 struct nfs_delegation *delegation;
55 int ret = 0;
56
57 flags &= FMODE_READ|FMODE_WRITE;
58 rcu_read_lock();
59 delegation = rcu_dereference(NFS_I(inode)->delegation);
60 if (delegation != NULL && (delegation->type & flags) == flags) {
61 nfs_mark_delegation_referenced(delegation);
62 ret = 1;
63 }
64 rcu_read_unlock();
65 return ret;
66}
67
Trond Myklebust888e6942005-11-04 15:38:11 -050068static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
69{
70 struct inode *inode = state->inode;
71 struct file_lock *fl;
Trond Myklebustd5122202009-06-17 13:22:58 -070072 int status = 0;
Trond Myklebust888e6942005-11-04 15:38:11 -050073
Trond Myklebust3f09df72009-06-17 13:23:00 -070074 if (inode->i_flock == NULL)
75 goto out;
76
77 /* Protect inode->i_flock using the BKL */
78 lock_kernel();
Harvey Harrison90dc7d22008-02-20 13:03:05 -080079 for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
Trond Myklebust888e6942005-11-04 15:38:11 -050080 if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
81 continue;
Trond Myklebustcd3758e2007-08-10 17:44:32 -040082 if (nfs_file_open_context(fl->fl_file) != ctx)
Trond Myklebust888e6942005-11-04 15:38:11 -050083 continue;
Trond Myklebust3f09df72009-06-17 13:23:00 -070084 unlock_kernel();
Trond Myklebust888e6942005-11-04 15:38:11 -050085 status = nfs4_lock_delegation_recall(state, fl);
Trond Myklebustd5122202009-06-17 13:22:58 -070086 if (status < 0)
Trond Myklebust3f09df72009-06-17 13:23:00 -070087 goto out;
88 lock_kernel();
Trond Myklebust888e6942005-11-04 15:38:11 -050089 }
Trond Myklebust3f09df72009-06-17 13:23:00 -070090 unlock_kernel();
91out:
Trond Myklebust888e6942005-11-04 15:38:11 -050092 return status;
93}
94
Trond Myklebust90163022007-07-05 14:55:18 -040095static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
Linus Torvalds1da177e2005-04-16 15:20:36 -070096{
97 struct nfs_inode *nfsi = NFS_I(inode);
98 struct nfs_open_context *ctx;
99 struct nfs4_state *state;
Trond Myklebust888e6942005-11-04 15:38:11 -0500100 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
102again:
103 spin_lock(&inode->i_lock);
104 list_for_each_entry(ctx, &nfsi->open_files, list) {
105 state = ctx->state;
106 if (state == NULL)
107 continue;
108 if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
109 continue;
Trond Myklebust90163022007-07-05 14:55:18 -0400110 if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0)
111 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 get_nfs_open_context(ctx);
113 spin_unlock(&inode->i_lock);
Trond Myklebust13437e12007-07-06 15:10:43 -0400114 err = nfs4_open_delegation_recall(ctx, state, stateid);
Trond Myklebust888e6942005-11-04 15:38:11 -0500115 if (err >= 0)
116 err = nfs_delegation_claim_locks(ctx, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 put_nfs_open_context(ctx);
Trond Myklebust888e6942005-11-04 15:38:11 -0500118 if (err != 0)
119 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 goto again;
121 }
122 spin_unlock(&inode->i_lock);
123}
124
125/*
126 * Set up a delegation on an inode
127 */
128void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
129{
130 struct nfs_delegation *delegation = NFS_I(inode)->delegation;
Trond Myklebust05c88ba2007-10-11 15:11:51 -0400131 struct rpc_cred *oldcred;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
133 if (delegation == NULL)
134 return;
135 memcpy(delegation->stateid.data, res->delegation.data,
136 sizeof(delegation->stateid.data));
137 delegation->type = res->delegation_type;
138 delegation->maxsize = res->maxsize;
Trond Myklebust05c88ba2007-10-11 15:11:51 -0400139 oldcred = delegation->cred;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 delegation->cred = get_rpccred(cred);
Trond Myklebust15c831b2008-12-23 15:21:39 -0500141 clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 NFS_I(inode)->delegation_state = delegation->type;
143 smp_wmb();
Trond Myklebust05c88ba2007-10-11 15:11:51 -0400144 put_rpccred(oldcred);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145}
146
Trond Myklebust57bfa892008-01-25 16:38:18 -0500147static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
148{
149 int res = 0;
150
151 res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
152 nfs_free_delegation(delegation);
153 return res;
154}
155
Trond Myklebust86e89482008-12-23 15:21:39 -0500156static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
157{
158 struct inode *inode = NULL;
159
160 spin_lock(&delegation->lock);
161 if (delegation->inode != NULL)
162 inode = igrab(delegation->inode);
163 spin_unlock(&delegation->lock);
164 return inode;
165}
166
Trond Myklebust57bfa892008-01-25 16:38:18 -0500167static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid)
168{
169 struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
170
171 if (delegation == NULL)
172 goto nomatch;
Trond Myklebust34310432008-12-23 15:21:38 -0500173 spin_lock(&delegation->lock);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500174 if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
175 sizeof(delegation->stateid.data)) != 0)
Trond Myklebust34310432008-12-23 15:21:38 -0500176 goto nomatch_unlock;
Trond Myklebust57bfa892008-01-25 16:38:18 -0500177 list_del_rcu(&delegation->super_list);
Trond Myklebust86e89482008-12-23 15:21:39 -0500178 delegation->inode = NULL;
Trond Myklebust57bfa892008-01-25 16:38:18 -0500179 nfsi->delegation_state = 0;
180 rcu_assign_pointer(nfsi->delegation, NULL);
Trond Myklebust34310432008-12-23 15:21:38 -0500181 spin_unlock(&delegation->lock);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500182 return delegation;
Trond Myklebust34310432008-12-23 15:21:38 -0500183nomatch_unlock:
184 spin_unlock(&delegation->lock);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500185nomatch:
186 return NULL;
187}
188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189/*
190 * Set up a delegation on an inode
191 */
192int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
193{
David Howells7539bba2006-08-22 20:06:09 -0400194 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 struct nfs_inode *nfsi = NFS_I(inode);
196 struct nfs_delegation *delegation;
Trond Myklebust57bfa892008-01-25 16:38:18 -0500197 struct nfs_delegation *freeme = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 int status = 0;
199
Panagiotis Issarisf52720c2006-09-27 01:49:39 -0700200 delegation = kmalloc(sizeof(*delegation), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 if (delegation == NULL)
202 return -ENOMEM;
203 memcpy(delegation->stateid.data, res->delegation.data,
204 sizeof(delegation->stateid.data));
205 delegation->type = res->delegation_type;
206 delegation->maxsize = res->maxsize;
Trond Myklebustbeb2a5e2006-01-03 09:55:37 +0100207 delegation->change_attr = nfsi->change_attr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 delegation->cred = get_rpccred(cred);
209 delegation->inode = inode;
Trond Myklebustb7391f42008-12-23 15:21:52 -0500210 delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
Trond Myklebust34310432008-12-23 15:21:38 -0500211 spin_lock_init(&delegation->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
213 spin_lock(&clp->cl_lock);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500214 if (rcu_dereference(nfsi->delegation) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 if (memcmp(&delegation->stateid, &nfsi->delegation->stateid,
Trond Myklebust57bfa892008-01-25 16:38:18 -0500216 sizeof(delegation->stateid)) == 0 &&
217 delegation->type == nfsi->delegation->type) {
218 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 }
Trond Myklebust57bfa892008-01-25 16:38:18 -0500220 /*
221 * Deal with broken servers that hand out two
222 * delegations for the same file.
223 */
224 dfprintk(FILE, "%s: server %s handed out "
225 "a duplicate delegation!\n",
Harvey Harrison3110ff82008-05-02 13:42:44 -0700226 __func__, clp->cl_hostname);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500227 if (delegation->type <= nfsi->delegation->type) {
228 freeme = delegation;
229 delegation = NULL;
230 goto out;
231 }
232 freeme = nfs_detach_delegation_locked(nfsi, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 }
Trond Myklebust57bfa892008-01-25 16:38:18 -0500234 list_add_rcu(&delegation->super_list, &clp->cl_delegations);
235 nfsi->delegation_state = delegation->type;
236 rcu_assign_pointer(nfsi->delegation, delegation);
237 delegation = NULL;
Trond Myklebust412c77c2007-07-03 16:10:55 -0400238
239 /* Ensure we revalidate the attributes and page cache! */
240 spin_lock(&inode->i_lock);
241 nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
242 spin_unlock(&inode->i_lock);
243
Trond Myklebust57bfa892008-01-25 16:38:18 -0500244out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 spin_unlock(&clp->cl_lock);
Trond Myklebust603c83d2007-10-18 19:59:20 -0400246 if (delegation != NULL)
247 nfs_free_delegation(delegation);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500248 if (freeme != NULL)
249 nfs_do_return_delegation(inode, freeme, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 return status;
251}
252
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253/* Sync all data to disk upon delegation return */
254static void nfs_msync_inode(struct inode *inode)
255{
256 filemap_fdatawrite(inode->i_mapping);
257 nfs_wb_all(inode);
258 filemap_fdatawait(inode->i_mapping);
259}
260
261/*
262 * Basic procedure for returning a delegation to the server
263 */
Trond Myklebust90163022007-07-05 14:55:18 -0400264static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 struct nfs_inode *nfsi = NFS_I(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
268 nfs_msync_inode(inode);
Trond Myklebust3f09df72009-06-17 13:23:00 -0700269 /*
270 * Guard against new delegated open/lock/unlock calls and against
271 * state recovery
272 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 down_write(&nfsi->rwsem);
Trond Myklebust90163022007-07-05 14:55:18 -0400274 nfs_delegation_claim_opens(inode, &delegation->stateid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 up_write(&nfsi->rwsem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 nfs_msync_inode(inode);
277
Trond Myklebuste6f81072008-01-24 18:14:34 -0500278 return nfs_do_return_delegation(inode, delegation, 1);
Trond Myklebust90163022007-07-05 14:55:18 -0400279}
280
Trond Myklebuste6f81072008-01-24 18:14:34 -0500281/*
Trond Myklebust515d8612008-12-23 15:21:46 -0500282 * Return all delegations that have been marked for return
283 */
Trond Myklebust707fb4b2008-12-23 15:21:47 -0500284void nfs_client_return_marked_delegations(struct nfs_client *clp)
Trond Myklebust515d8612008-12-23 15:21:46 -0500285{
286 struct nfs_delegation *delegation;
287 struct inode *inode;
288
289restart:
290 rcu_read_lock();
291 list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
292 if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
293 continue;
294 inode = nfs_delegation_grab_inode(delegation);
295 if (inode == NULL)
296 continue;
297 spin_lock(&clp->cl_lock);
298 delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
299 spin_unlock(&clp->cl_lock);
300 rcu_read_unlock();
301 if (delegation != NULL)
302 __nfs_inode_return_delegation(inode, delegation);
303 iput(inode);
304 goto restart;
305 }
306 rcu_read_unlock();
307}
308
309/*
Trond Myklebuste6f81072008-01-24 18:14:34 -0500310 * This function returns the delegation without reclaiming opens
311 * or protecting against delegation reclaims.
312 * It is therefore really only safe to be called from
313 * nfs4_clear_inode()
314 */
315void nfs_inode_return_delegation_noreclaim(struct inode *inode)
316{
317 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
318 struct nfs_inode *nfsi = NFS_I(inode);
319 struct nfs_delegation *delegation;
320
321 if (rcu_dereference(nfsi->delegation) != NULL) {
322 spin_lock(&clp->cl_lock);
323 delegation = nfs_detach_delegation_locked(nfsi, NULL);
324 spin_unlock(&clp->cl_lock);
325 if (delegation != NULL)
326 nfs_do_return_delegation(inode, delegation, 0);
327 }
328}
329
Trond Myklebust90163022007-07-05 14:55:18 -0400330int nfs_inode_return_delegation(struct inode *inode)
331{
332 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
333 struct nfs_inode *nfsi = NFS_I(inode);
334 struct nfs_delegation *delegation;
335 int err = 0;
336
Trond Myklebust8383e462007-07-06 15:12:04 -0400337 if (rcu_dereference(nfsi->delegation) != NULL) {
Trond Myklebust90163022007-07-05 14:55:18 -0400338 spin_lock(&clp->cl_lock);
339 delegation = nfs_detach_delegation_locked(nfsi, NULL);
340 spin_unlock(&clp->cl_lock);
341 if (delegation != NULL)
342 err = __nfs_inode_return_delegation(inode, delegation);
343 }
344 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345}
346
Trond Myklebust6411bd42008-12-23 15:21:51 -0500347static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation)
348{
349 set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
350 set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
351}
352
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353/*
354 * Return all delegations associated to a super block
355 */
Trond Myklebust515d8612008-12-23 15:21:46 -0500356void nfs_super_return_all_delegations(struct super_block *sb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357{
David Howells7539bba2006-08-22 20:06:09 -0400358 struct nfs_client *clp = NFS_SB(sb)->nfs_client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 struct nfs_delegation *delegation;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360
361 if (clp == NULL)
362 return;
Trond Myklebust8383e462007-07-06 15:12:04 -0400363 rcu_read_lock();
364 list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
Trond Myklebust86e89482008-12-23 15:21:39 -0500365 spin_lock(&delegation->lock);
366 if (delegation->inode != NULL && delegation->inode->i_sb == sb)
Trond Myklebust515d8612008-12-23 15:21:46 -0500367 set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
Trond Myklebust86e89482008-12-23 15:21:39 -0500368 spin_unlock(&delegation->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 }
Trond Myklebust8383e462007-07-06 15:12:04 -0400370 rcu_read_unlock();
Trond Myklebust515d8612008-12-23 15:21:46 -0500371 nfs_client_return_marked_delegations(clp);
372}
373
Trond Myklebust707fb4b2008-12-23 15:21:47 -0500374static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
Trond Myklebust515d8612008-12-23 15:21:46 -0500375{
376 struct nfs_delegation *delegation;
377
378 rcu_read_lock();
Trond Myklebust707fb4b2008-12-23 15:21:47 -0500379 list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
Trond Myklebust515d8612008-12-23 15:21:46 -0500380 set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
Trond Myklebust707fb4b2008-12-23 15:21:47 -0500381 set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
382 }
Trond Myklebust515d8612008-12-23 15:21:46 -0500383 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384}
385
Trond Myklebustb0d3ded2008-12-23 15:21:50 -0500386static void nfs_delegation_run_state_manager(struct nfs_client *clp)
Trond Myklebust58d97142006-01-03 09:55:24 +0100387{
Trond Myklebustb0d3ded2008-12-23 15:21:50 -0500388 if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
389 nfs4_schedule_state_manager(clp);
Trond Myklebust58d97142006-01-03 09:55:24 +0100390}
391
David Howellsadfa6f92006-08-22 20:06:08 -0400392void nfs_expire_all_delegations(struct nfs_client *clp)
Trond Myklebust58d97142006-01-03 09:55:24 +0100393{
Trond Myklebustb0d3ded2008-12-23 15:21:50 -0500394 nfs_client_mark_return_all_delegations(clp);
395 nfs_delegation_run_state_manager(clp);
Trond Myklebust58d97142006-01-03 09:55:24 +0100396}
397
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398/*
399 * Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
400 */
David Howellsadfa6f92006-08-22 20:06:08 -0400401void nfs_handle_cb_pathdown(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 if (clp == NULL)
404 return;
Trond Myklebust707fb4b2008-12-23 15:21:47 -0500405 nfs_client_mark_return_all_delegations(clp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406}
407
Trond Myklebustb7391f42008-12-23 15:21:52 -0500408static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp)
409{
410 struct nfs_delegation *delegation;
411
412 rcu_read_lock();
413 list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
414 if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
415 continue;
416 set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
417 set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
418 }
419 rcu_read_unlock();
420}
421
422void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
423{
424 nfs_client_mark_return_unreferenced_delegations(clp);
425 nfs_delegation_run_state_manager(clp);
426}
427
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428/*
429 * Asynchronous delegation recall!
430 */
431int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
432{
Trond Myklebust6411bd42008-12-23 15:21:51 -0500433 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
434 struct nfs_delegation *delegation;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
Trond Myklebust6411bd42008-12-23 15:21:51 -0500436 rcu_read_lock();
437 delegation = rcu_dereference(NFS_I(inode)->delegation);
438 if (delegation == NULL || memcmp(delegation->stateid.data, stateid->data,
439 sizeof(delegation->stateid.data)) != 0) {
440 rcu_read_unlock();
441 return -ENOENT;
442 }
443 nfs_mark_return_delegation(clp, delegation);
444 rcu_read_unlock();
445 nfs_delegation_run_state_manager(clp);
446 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447}
448
449/*
450 * Retrieve the inode associated with a delegation
451 */
David Howellsadfa6f92006-08-22 20:06:08 -0400452struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453{
454 struct nfs_delegation *delegation;
455 struct inode *res = NULL;
Trond Myklebust8383e462007-07-06 15:12:04 -0400456 rcu_read_lock();
457 list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
Trond Myklebust86e89482008-12-23 15:21:39 -0500458 spin_lock(&delegation->lock);
459 if (delegation->inode != NULL &&
460 nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 res = igrab(delegation->inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 }
Trond Myklebust86e89482008-12-23 15:21:39 -0500463 spin_unlock(&delegation->lock);
464 if (res != NULL)
465 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 }
Trond Myklebust8383e462007-07-06 15:12:04 -0400467 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 return res;
469}
470
471/*
472 * Mark all delegations as needing to be reclaimed
473 */
David Howellsadfa6f92006-08-22 20:06:08 -0400474void nfs_delegation_mark_reclaim(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475{
476 struct nfs_delegation *delegation;
Trond Myklebust8383e462007-07-06 15:12:04 -0400477 rcu_read_lock();
478 list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list)
Trond Myklebust15c831b2008-12-23 15:21:39 -0500479 set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
Trond Myklebust8383e462007-07-06 15:12:04 -0400480 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481}
482
483/*
484 * Reap all unclaimed delegations after reboot recovery is done
485 */
David Howellsadfa6f92006-08-22 20:06:08 -0400486void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
Trond Myklebust8383e462007-07-06 15:12:04 -0400488 struct nfs_delegation *delegation;
Trond Myklebust86e89482008-12-23 15:21:39 -0500489 struct inode *inode;
Trond Myklebust8383e462007-07-06 15:12:04 -0400490restart:
491 rcu_read_lock();
492 list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
Trond Myklebust15c831b2008-12-23 15:21:39 -0500493 if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 continue;
Trond Myklebust86e89482008-12-23 15:21:39 -0500495 inode = nfs_delegation_grab_inode(delegation);
496 if (inode == NULL)
497 continue;
Trond Myklebust8383e462007-07-06 15:12:04 -0400498 spin_lock(&clp->cl_lock);
Trond Myklebust86e89482008-12-23 15:21:39 -0500499 delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
Trond Myklebust8383e462007-07-06 15:12:04 -0400500 spin_unlock(&clp->cl_lock);
501 rcu_read_unlock();
502 if (delegation != NULL)
Trond Myklebust905f8d12007-08-06 12:18:34 -0400503 nfs_free_delegation(delegation);
Trond Myklebust86e89482008-12-23 15:21:39 -0500504 iput(inode);
Trond Myklebust8383e462007-07-06 15:12:04 -0400505 goto restart;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 }
Trond Myklebust8383e462007-07-06 15:12:04 -0400507 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508}
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500509
510int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
511{
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500512 struct nfs_inode *nfsi = NFS_I(inode);
513 struct nfs_delegation *delegation;
Trond Myklebust8383e462007-07-06 15:12:04 -0400514 int ret = 0;
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500515
Trond Myklebust8383e462007-07-06 15:12:04 -0400516 rcu_read_lock();
517 delegation = rcu_dereference(nfsi->delegation);
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500518 if (delegation != NULL) {
519 memcpy(dst->data, delegation->stateid.data, sizeof(dst->data));
Trond Myklebust8383e462007-07-06 15:12:04 -0400520 ret = 1;
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500521 }
Trond Myklebust8383e462007-07-06 15:12:04 -0400522 rcu_read_unlock();
523 return ret;
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500524}