blob: ef0c394b7bf55cc5891e01fc4647acff2db41307 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090013#include <linux/slab.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_free_delegation(struct nfs_delegation *delegation)
25{
Trond Myklebuste00b8a22011-01-27 14:55:39 -050026 if (delegation->cred) {
27 put_rpccred(delegation->cred);
28 delegation->cred = NULL;
29 }
Lai Jiangshan26f04dd2011-05-01 06:21:54 -070030 kfree_rcu(delegation, rcu);
Trond Myklebust8383e462007-07-06 15:12:04 -040031}
32
Chuck Leverd3978bb2010-12-24 01:33:04 +000033/**
34 * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
35 * @delegation: delegation to process
36 *
37 */
Trond Myklebustb7391f42008-12-23 15:21:52 -050038void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
39{
40 set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
41}
42
Chuck Leverd3978bb2010-12-24 01:33:04 +000043/**
44 * nfs_have_delegation - check if inode has a delegation
45 * @inode: inode to check
46 * @flags: delegation types to check for
47 *
48 * Returns one if inode has the indicated delegation, otherwise zero.
49 */
Bryan Schumaker011e2a72012-06-20 15:53:43 -040050int nfs4_have_delegation(struct inode *inode, fmode_t flags)
Trond Myklebustb7391f42008-12-23 15:21:52 -050051{
52 struct nfs_delegation *delegation;
53 int ret = 0;
54
55 flags &= FMODE_READ|FMODE_WRITE;
56 rcu_read_lock();
57 delegation = rcu_dereference(NFS_I(inode)->delegation);
Trond Myklebustd25be542013-02-05 11:43:28 -050058 if (delegation != NULL && (delegation->type & flags) == flags &&
59 !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
Trond Myklebustb7391f42008-12-23 15:21:52 -050060 nfs_mark_delegation_referenced(delegation);
61 ret = 1;
62 }
63 rcu_read_unlock();
64 return ret;
65}
66
Trond Myklebustdb4f2e62013-04-01 15:56:46 -040067static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
Trond Myklebust888e6942005-11-04 15:38:11 -050068{
69 struct inode *inode = state->inode;
70 struct file_lock *fl;
Trond Myklebustd5122202009-06-17 13:22:58 -070071 int status = 0;
Trond Myklebust888e6942005-11-04 15:38:11 -050072
Trond Myklebust3f09df72009-06-17 13:23:00 -070073 if (inode->i_flock == NULL)
Trond Myklebust65b62a22013-02-07 10:54:07 -050074 goto out;
Jeff Layton314d7cc2013-04-10 15:36:48 -040075
Arnd Bergmannb89f4322010-09-18 15:09:31 +020076 /* Protect inode->i_flock using the file locks lock */
77 lock_flocks();
Harvey Harrison90dc7d22008-02-20 13:03:05 -080078 for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
Trond Myklebust888e6942005-11-04 15:38:11 -050079 if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
80 continue;
Trond Myklebustcd3758e2007-08-10 17:44:32 -040081 if (nfs_file_open_context(fl->fl_file) != ctx)
Trond Myklebust888e6942005-11-04 15:38:11 -050082 continue;
Arnd Bergmannb89f4322010-09-18 15:09:31 +020083 unlock_flocks();
Trond Myklebustdb4f2e62013-04-01 15:56:46 -040084 status = nfs4_lock_delegation_recall(fl, state, stateid);
Trond Myklebustd5122202009-06-17 13:22:58 -070085 if (status < 0)
Trond Myklebust3f09df72009-06-17 13:23:00 -070086 goto out;
Arnd Bergmannb89f4322010-09-18 15:09:31 +020087 lock_flocks();
Trond Myklebust888e6942005-11-04 15:38:11 -050088 }
Arnd Bergmannb89f4322010-09-18 15:09:31 +020089 unlock_flocks();
Trond Myklebust3f09df72009-06-17 13:23:00 -070090out:
Trond Myklebust888e6942005-11-04 15:38:11 -050091 return status;
92}
93
Trond Myklebustd18cc1f2009-12-03 08:10:17 -050094static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095{
96 struct nfs_inode *nfsi = NFS_I(inode);
97 struct nfs_open_context *ctx;
Trond Myklebustd25be542013-02-05 11:43:28 -050098 struct nfs4_state_owner *sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 struct nfs4_state *state;
Trond Myklebustd25be542013-02-05 11:43:28 -0500100 unsigned int seq;
Trond Myklebust888e6942005-11-04 15:38:11 -0500101 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
103again:
104 spin_lock(&inode->i_lock);
105 list_for_each_entry(ctx, &nfsi->open_files, list) {
106 state = ctx->state;
107 if (state == NULL)
108 continue;
109 if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
110 continue;
Trond Myklebustbc2075d2014-10-17 23:02:52 +0300111 if (!nfs4_valid_open_stateid(state))
112 continue;
Trond Myklebustf597c532012-03-04 18:13:56 -0500113 if (!nfs4_stateid_match(&state->stateid, stateid))
Trond Myklebust90163022007-07-05 14:55:18 -0400114 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 get_nfs_open_context(ctx);
116 spin_unlock(&inode->i_lock);
Trond Myklebustd25be542013-02-05 11:43:28 -0500117 sp = state->owner;
Trond Myklebust65b62a22013-02-07 10:54:07 -0500118 /* Block nfs4_proc_unlck */
119 mutex_lock(&sp->so_delegreturn_mutex);
Trond Myklebustd25be542013-02-05 11:43:28 -0500120 seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
Trond Myklebust13437e12007-07-06 15:10:43 -0400121 err = nfs4_open_delegation_recall(ctx, state, stateid);
Trond Myklebustd25be542013-02-05 11:43:28 -0500122 if (!err)
Trond Myklebustdb4f2e62013-04-01 15:56:46 -0400123 err = nfs_delegation_claim_locks(ctx, state, stateid);
Trond Myklebustd25be542013-02-05 11:43:28 -0500124 if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
125 err = -EAGAIN;
Trond Myklebust65b62a22013-02-07 10:54:07 -0500126 mutex_unlock(&sp->so_delegreturn_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 put_nfs_open_context(ctx);
Trond Myklebust888e6942005-11-04 15:38:11 -0500128 if (err != 0)
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500129 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 goto again;
131 }
132 spin_unlock(&inode->i_lock);
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500133 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134}
135
Chuck Leverd3978bb2010-12-24 01:33:04 +0000136/**
137 * nfs_inode_reclaim_delegation - process a delegation reclaim request
138 * @inode: inode to process
139 * @cred: credential to use for request
140 * @res: new delegation state from server
141 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 */
Chuck Leverd3978bb2010-12-24 01:33:04 +0000143void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
144 struct nfs_openres *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145{
Trond Myklebust8f649c32010-05-01 12:36:18 -0400146 struct nfs_delegation *delegation;
147 struct rpc_cred *oldcred = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
Trond Myklebust8f649c32010-05-01 12:36:18 -0400149 rcu_read_lock();
150 delegation = rcu_dereference(NFS_I(inode)->delegation);
151 if (delegation != NULL) {
152 spin_lock(&delegation->lock);
153 if (delegation->inode != NULL) {
Trond Myklebustf597c532012-03-04 18:13:56 -0500154 nfs4_stateid_copy(&delegation->stateid, &res->delegation);
Trond Myklebust8f649c32010-05-01 12:36:18 -0400155 delegation->type = res->delegation_type;
156 delegation->maxsize = res->maxsize;
157 oldcred = delegation->cred;
158 delegation->cred = get_rpccred(cred);
159 clear_bit(NFS_DELEGATION_NEED_RECLAIM,
160 &delegation->flags);
161 NFS_I(inode)->delegation_state = delegation->type;
162 spin_unlock(&delegation->lock);
163 put_rpccred(oldcred);
164 rcu_read_unlock();
165 } else {
166 /* We appear to have raced with a delegation return. */
167 spin_unlock(&delegation->lock);
168 rcu_read_unlock();
169 nfs_inode_set_delegation(inode, cred, res);
170 }
171 } else {
172 rcu_read_unlock();
173 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174}
175
Trond Myklebust57bfa892008-01-25 16:38:18 -0500176static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
177{
178 int res = 0;
179
Trond Myklebust270e2342014-11-10 18:43:56 -0500180 if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
181 res = nfs4_proc_delegreturn(inode,
182 delegation->cred,
183 &delegation->stateid,
184 issync);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500185 nfs_free_delegation(delegation);
186 return res;
187}
188
Trond Myklebust86e89482008-12-23 15:21:39 -0500189static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
190{
191 struct inode *inode = NULL;
192
193 spin_lock(&delegation->lock);
194 if (delegation->inode != NULL)
195 inode = igrab(delegation->inode);
196 spin_unlock(&delegation->lock);
197 return inode;
198}
199
Chuck Leverdda4b222010-12-24 01:32:54 +0000200static struct nfs_delegation *
Trond Myklebustd25be542013-02-05 11:43:28 -0500201nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
Trond Myklebust57bfa892008-01-25 16:38:18 -0500202{
Trond Myklebustd25be542013-02-05 11:43:28 -0500203 struct nfs_delegation *ret = NULL;
204 struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500205
206 if (delegation == NULL)
Trond Myklebustd25be542013-02-05 11:43:28 -0500207 goto out;
208 spin_lock(&delegation->lock);
209 if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
210 ret = delegation;
211 spin_unlock(&delegation->lock);
212out:
213 return ret;
214}
215
216static struct nfs_delegation *
217nfs_start_delegation_return(struct nfs_inode *nfsi)
218{
219 struct nfs_delegation *delegation;
220
221 rcu_read_lock();
222 delegation = nfs_start_delegation_return_locked(nfsi);
223 rcu_read_unlock();
224 return delegation;
225}
226
227static void
228nfs_abort_delegation_return(struct nfs_delegation *delegation,
229 struct nfs_client *clp)
230{
Chuck Leverdda4b222010-12-24 01:32:54 +0000231
Trond Myklebust34310432008-12-23 15:21:38 -0500232 spin_lock(&delegation->lock);
Trond Myklebustd25be542013-02-05 11:43:28 -0500233 clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
234 set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
235 spin_unlock(&delegation->lock);
236 set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
237}
238
239static struct nfs_delegation *
240nfs_detach_delegation_locked(struct nfs_inode *nfsi,
241 struct nfs_delegation *delegation,
242 struct nfs_client *clp)
243{
244 struct nfs_delegation *deleg_cur =
245 rcu_dereference_protected(nfsi->delegation,
246 lockdep_is_held(&clp->cl_lock));
247
248 if (deleg_cur == NULL || delegation != deleg_cur)
249 return NULL;
250
251 spin_lock(&delegation->lock);
252 set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500253 list_del_rcu(&delegation->super_list);
Trond Myklebust86e89482008-12-23 15:21:39 -0500254 delegation->inode = NULL;
Trond Myklebust57bfa892008-01-25 16:38:18 -0500255 nfsi->delegation_state = 0;
256 rcu_assign_pointer(nfsi->delegation, NULL);
Trond Myklebust34310432008-12-23 15:21:38 -0500257 spin_unlock(&delegation->lock);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500258 return delegation;
Trond Myklebust57bfa892008-01-25 16:38:18 -0500259}
260
Chuck Leverdda4b222010-12-24 01:32:54 +0000261static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
Trond Myklebustd25be542013-02-05 11:43:28 -0500262 struct nfs_delegation *delegation,
263 struct nfs_server *server)
Chuck Leverdda4b222010-12-24 01:32:54 +0000264{
Chuck Leverd3978bb2010-12-24 01:33:04 +0000265 struct nfs_client *clp = server->nfs_client;
Chuck Leverdda4b222010-12-24 01:32:54 +0000266
267 spin_lock(&clp->cl_lock);
Trond Myklebustd25be542013-02-05 11:43:28 -0500268 delegation = nfs_detach_delegation_locked(nfsi, delegation, clp);
Chuck Leverdda4b222010-12-24 01:32:54 +0000269 spin_unlock(&clp->cl_lock);
270 return delegation;
271}
272
Trond Myklebustd25be542013-02-05 11:43:28 -0500273static struct nfs_delegation *
274nfs_inode_detach_delegation(struct inode *inode)
275{
276 struct nfs_inode *nfsi = NFS_I(inode);
277 struct nfs_server *server = NFS_SERVER(inode);
278 struct nfs_delegation *delegation;
279
280 delegation = nfs_start_delegation_return(nfsi);
281 if (delegation == NULL)
282 return NULL;
283 return nfs_detach_delegation(nfsi, delegation, server);
284}
285
Chuck Leverd3978bb2010-12-24 01:33:04 +0000286/**
287 * nfs_inode_set_delegation - set up a delegation on an inode
288 * @inode: inode to which delegation applies
289 * @cred: cred to use for subsequent delegation processing
290 * @res: new delegation state from server
291 *
292 * Returns zero on success, or a negative errno value.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 */
294int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
295{
Chuck Leverd3978bb2010-12-24 01:33:04 +0000296 struct nfs_server *server = NFS_SERVER(inode);
297 struct nfs_client *clp = server->nfs_client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 struct nfs_inode *nfsi = NFS_I(inode);
David Howells17d2c0a2010-05-01 12:37:18 -0400299 struct nfs_delegation *delegation, *old_delegation;
Trond Myklebust57bfa892008-01-25 16:38:18 -0500300 struct nfs_delegation *freeme = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 int status = 0;
302
Trond Myklebust8535b2b2010-05-13 12:51:01 -0400303 delegation = kmalloc(sizeof(*delegation), GFP_NOFS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 if (delegation == NULL)
305 return -ENOMEM;
Trond Myklebustf597c532012-03-04 18:13:56 -0500306 nfs4_stateid_copy(&delegation->stateid, &res->delegation);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 delegation->type = res->delegation_type;
308 delegation->maxsize = res->maxsize;
Trond Myklebusta9a4a872011-10-17 16:08:46 -0700309 delegation->change_attr = inode->i_version;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 delegation->cred = get_rpccred(cred);
311 delegation->inode = inode;
Trond Myklebustb7391f42008-12-23 15:21:52 -0500312 delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
Trond Myklebust34310432008-12-23 15:21:38 -0500313 spin_lock_init(&delegation->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
315 spin_lock(&clp->cl_lock);
David Howells17d2c0a2010-05-01 12:37:18 -0400316 old_delegation = rcu_dereference_protected(nfsi->delegation,
Chuck Leverd3978bb2010-12-24 01:33:04 +0000317 lockdep_is_held(&clp->cl_lock));
David Howells17d2c0a2010-05-01 12:37:18 -0400318 if (old_delegation != NULL) {
Trond Myklebustf597c532012-03-04 18:13:56 -0500319 if (nfs4_stateid_match(&delegation->stateid,
320 &old_delegation->stateid) &&
David Howells17d2c0a2010-05-01 12:37:18 -0400321 delegation->type == old_delegation->type) {
Trond Myklebust57bfa892008-01-25 16:38:18 -0500322 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 }
Trond Myklebust57bfa892008-01-25 16:38:18 -0500324 /*
325 * Deal with broken servers that hand out two
326 * delegations for the same file.
Trond Myklebust17280172012-03-11 13:11:00 -0400327 * Allow for upgrades to a WRITE delegation, but
328 * nothing else.
Trond Myklebust57bfa892008-01-25 16:38:18 -0500329 */
330 dfprintk(FILE, "%s: server %s handed out "
331 "a duplicate delegation!\n",
Harvey Harrison3110ff82008-05-02 13:42:44 -0700332 __func__, clp->cl_hostname);
Trond Myklebust17280172012-03-11 13:11:00 -0400333 if (delegation->type == old_delegation->type ||
334 !(delegation->type & FMODE_WRITE)) {
Trond Myklebust57bfa892008-01-25 16:38:18 -0500335 freeme = delegation;
336 delegation = NULL;
337 goto out;
338 }
Trond Myklebustd25be542013-02-05 11:43:28 -0500339 freeme = nfs_detach_delegation_locked(nfsi,
340 old_delegation, clp);
341 if (freeme == NULL)
342 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 }
Chuck Leverd3978bb2010-12-24 01:33:04 +0000344 list_add_rcu(&delegation->super_list, &server->delegations);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500345 nfsi->delegation_state = delegation->type;
346 rcu_assign_pointer(nfsi->delegation, delegation);
347 delegation = NULL;
Trond Myklebust412c77c2007-07-03 16:10:55 -0400348
349 /* Ensure we revalidate the attributes and page cache! */
350 spin_lock(&inode->i_lock);
351 nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
352 spin_unlock(&inode->i_lock);
353
Trond Myklebust57bfa892008-01-25 16:38:18 -0500354out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 spin_unlock(&clp->cl_lock);
Trond Myklebust603c83d2007-10-18 19:59:20 -0400356 if (delegation != NULL)
357 nfs_free_delegation(delegation);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500358 if (freeme != NULL)
359 nfs_do_return_delegation(inode, freeme, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 return status;
361}
362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363/*
364 * Basic procedure for returning a delegation to the server
365 */
Trond Myklebustd25be542013-02-05 11:43:28 -0500366static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367{
Trond Myklebustd25be542013-02-05 11:43:28 -0500368 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 struct nfs_inode *nfsi = NFS_I(inode);
Trond Myklebust270e2342014-11-10 18:43:56 -0500370 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Trond Myklebustd25be542013-02-05 11:43:28 -0500372 if (delegation == NULL)
373 return 0;
374 do {
Trond Myklebust270e2342014-11-10 18:43:56 -0500375 if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
376 break;
Trond Myklebustd25be542013-02-05 11:43:28 -0500377 err = nfs_delegation_claim_opens(inode, &delegation->stateid);
378 if (!issync || err != -EAGAIN)
379 break;
380 /*
381 * Guard against state recovery
382 */
383 err = nfs4_wait_clnt_recover(clp);
384 } while (err == 0);
385
386 if (err) {
387 nfs_abort_delegation_return(delegation, clp);
388 goto out;
389 }
390 if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode)))
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500391 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500393 err = nfs_do_return_delegation(inode, delegation, issync);
394out:
395 return err;
Trond Myklebust90163022007-07-05 14:55:18 -0400396}
397
Trond Myklebustb7571442013-04-03 14:33:49 -0400398static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
399{
400 bool ret = false;
401
402 if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
403 ret = true;
404 if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) {
405 struct inode *inode;
406
407 spin_lock(&delegation->lock);
408 inode = delegation->inode;
409 if (inode && list_empty(&NFS_I(inode)->open_files))
410 ret = true;
411 spin_unlock(&delegation->lock);
412 }
413 return ret;
414}
415
Chuck Leverd3978bb2010-12-24 01:33:04 +0000416/**
417 * nfs_client_return_marked_delegations - return previously marked delegations
418 * @clp: nfs_client to process
419 *
Trond Myklebustdc327ed2012-05-06 19:46:30 -0400420 * Note that this function is designed to be called by the state
421 * manager thread. For this reason, it cannot flush the dirty data,
422 * since that could deadlock in case of a state recovery error.
423 *
Chuck Leverd3978bb2010-12-24 01:33:04 +0000424 * Returns zero on success, or a negative errno value.
Trond Myklebust515d8612008-12-23 15:21:46 -0500425 */
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500426int nfs_client_return_marked_delegations(struct nfs_client *clp)
Trond Myklebust515d8612008-12-23 15:21:46 -0500427{
428 struct nfs_delegation *delegation;
Chuck Leverd3978bb2010-12-24 01:33:04 +0000429 struct nfs_server *server;
Trond Myklebust515d8612008-12-23 15:21:46 -0500430 struct inode *inode;
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500431 int err = 0;
Trond Myklebust515d8612008-12-23 15:21:46 -0500432
433restart:
434 rcu_read_lock();
Chuck Leverd3978bb2010-12-24 01:33:04 +0000435 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
436 list_for_each_entry_rcu(delegation, &server->delegations,
437 super_list) {
Trond Myklebustb7571442013-04-03 14:33:49 -0400438 if (!nfs_delegation_need_return(delegation))
Chuck Leverd3978bb2010-12-24 01:33:04 +0000439 continue;
440 inode = nfs_delegation_grab_inode(delegation);
441 if (inode == NULL)
442 continue;
Trond Myklebustd25be542013-02-05 11:43:28 -0500443 delegation = nfs_start_delegation_return_locked(NFS_I(inode));
Chuck Leverd3978bb2010-12-24 01:33:04 +0000444 rcu_read_unlock();
445
Trond Myklebustd25be542013-02-05 11:43:28 -0500446 err = nfs_end_delegation_return(inode, delegation, 0);
Chuck Leverd3978bb2010-12-24 01:33:04 +0000447 iput(inode);
448 if (!err)
449 goto restart;
450 set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
451 return err;
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500452 }
Trond Myklebust515d8612008-12-23 15:21:46 -0500453 }
454 rcu_read_unlock();
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500455 return 0;
Trond Myklebust515d8612008-12-23 15:21:46 -0500456}
457
Chuck Leverd3978bb2010-12-24 01:33:04 +0000458/**
459 * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens
460 * @inode: inode to process
461 *
462 * Does not protect against delegation reclaims, therefore really only safe
463 * to be called from nfs4_clear_inode().
Trond Myklebuste6f81072008-01-24 18:14:34 -0500464 */
465void nfs_inode_return_delegation_noreclaim(struct inode *inode)
466{
Trond Myklebuste6f81072008-01-24 18:14:34 -0500467 struct nfs_delegation *delegation;
468
Trond Myklebustd25be542013-02-05 11:43:28 -0500469 delegation = nfs_inode_detach_delegation(inode);
470 if (delegation != NULL)
471 nfs_do_return_delegation(inode, delegation, 0);
Trond Myklebuste6f81072008-01-24 18:14:34 -0500472}
473
Chuck Leverd3978bb2010-12-24 01:33:04 +0000474/**
475 * nfs_inode_return_delegation - synchronously return a delegation
476 * @inode: inode to process
477 *
Trond Myklebustc57d1bc2012-05-06 19:34:17 -0400478 * This routine will always flush any dirty data to disk on the
479 * assumption that if we need to return the delegation, then
480 * we should stop caching.
481 *
Chuck Leverd3978bb2010-12-24 01:33:04 +0000482 * Returns zero on success, or a negative errno value.
483 */
Bryan Schumaker57ec14c2012-06-20 15:53:44 -0400484int nfs4_inode_return_delegation(struct inode *inode)
Trond Myklebust90163022007-07-05 14:55:18 -0400485{
Trond Myklebust90163022007-07-05 14:55:18 -0400486 struct nfs_inode *nfsi = NFS_I(inode);
487 struct nfs_delegation *delegation;
488 int err = 0;
489
Trond Myklebustc57d1bc2012-05-06 19:34:17 -0400490 nfs_wb_all(inode);
Trond Myklebustd25be542013-02-05 11:43:28 -0500491 delegation = nfs_start_delegation_return(nfsi);
492 if (delegation != NULL)
493 err = nfs_end_delegation_return(inode, delegation, 1);
Trond Myklebust90163022007-07-05 14:55:18 -0400494 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495}
496
Trond Myklebustb7571442013-04-03 14:33:49 -0400497static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
498 struct nfs_delegation *delegation)
499{
500 set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
501 set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
502}
503
Trond Myklebusted1e62112011-07-25 15:37:29 -0400504static void nfs_mark_return_delegation(struct nfs_server *server,
505 struct nfs_delegation *delegation)
Trond Myklebust6411bd42008-12-23 15:21:51 -0500506{
507 set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
Trond Myklebusted1e62112011-07-25 15:37:29 -0400508 set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
Trond Myklebust6411bd42008-12-23 15:21:51 -0500509}
510
Trond Myklebust5c31e232013-04-03 19:04:58 -0400511static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
512{
513 struct nfs_delegation *delegation;
514 bool ret = false;
515
516 list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
517 nfs_mark_return_delegation(server, delegation);
518 ret = true;
519 }
520 return ret;
521}
522
Trond Myklebustb02ba0b2013-04-03 19:23:58 -0400523static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
524{
525 struct nfs_server *server;
526
527 rcu_read_lock();
528 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
529 nfs_server_mark_return_all_delegations(server);
530 rcu_read_unlock();
531}
532
533static void nfs_delegation_run_state_manager(struct nfs_client *clp)
534{
535 if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
536 nfs4_schedule_state_manager(clp);
537}
538
539/**
540 * nfs_expire_all_delegations
541 * @clp: client to process
542 *
543 */
544void nfs_expire_all_delegations(struct nfs_client *clp)
545{
546 nfs_client_mark_return_all_delegations(clp);
547 nfs_delegation_run_state_manager(clp);
548}
549
Chuck Leverd3978bb2010-12-24 01:33:04 +0000550/**
551 * nfs_super_return_all_delegations - return delegations for one superblock
552 * @sb: sb to process
553 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 */
Bryan Schumakereeebf912012-06-20 15:53:41 -0400555void nfs_server_return_all_delegations(struct nfs_server *server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556{
Chuck Leverd3978bb2010-12-24 01:33:04 +0000557 struct nfs_client *clp = server->nfs_client;
Trond Myklebust5c31e232013-04-03 19:04:58 -0400558 bool need_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
560 if (clp == NULL)
561 return;
Chuck Leverd3978bb2010-12-24 01:33:04 +0000562
Trond Myklebust8383e462007-07-06 15:12:04 -0400563 rcu_read_lock();
Trond Myklebust5c31e232013-04-03 19:04:58 -0400564 need_wait = nfs_server_mark_return_all_delegations(server);
Trond Myklebust8383e462007-07-06 15:12:04 -0400565 rcu_read_unlock();
Chuck Leverd3978bb2010-12-24 01:33:04 +0000566
Trond Myklebust5c31e232013-04-03 19:04:58 -0400567 if (need_wait) {
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500568 nfs4_schedule_state_manager(clp);
Trond Myklebust5c31e232013-04-03 19:04:58 -0400569 nfs4_wait_clnt_recover(clp);
570 }
Trond Myklebust515d8612008-12-23 15:21:46 -0500571}
572
Trond Myklebust826e0012013-04-03 19:27:52 -0400573static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,
Chuck Leverd3978bb2010-12-24 01:33:04 +0000574 fmode_t flags)
Trond Myklebust515d8612008-12-23 15:21:46 -0500575{
576 struct nfs_delegation *delegation;
577
Chuck Leverd3978bb2010-12-24 01:33:04 +0000578 list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
Alexandros Batsakisc79571a2009-12-05 13:20:52 -0500579 if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
580 continue;
581 if (delegation->type & flags)
Trond Myklebust826e0012013-04-03 19:27:52 -0400582 nfs_mark_return_if_closed_delegation(server, delegation);
Trond Myklebust707fb4b2008-12-23 15:21:47 -0500583 }
Chuck Leverd3978bb2010-12-24 01:33:04 +0000584}
585
Trond Myklebust826e0012013-04-03 19:27:52 -0400586static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,
Chuck Leverd3978bb2010-12-24 01:33:04 +0000587 fmode_t flags)
588{
589 struct nfs_server *server;
590
591 rcu_read_lock();
592 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
Trond Myklebust826e0012013-04-03 19:27:52 -0400593 nfs_mark_return_unused_delegation_types(server, flags);
Trond Myklebust515d8612008-12-23 15:21:46 -0500594 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595}
596
Trond Myklebust270e2342014-11-10 18:43:56 -0500597static void nfs_revoke_delegation(struct inode *inode)
598{
599 struct nfs_delegation *delegation;
600 rcu_read_lock();
601 delegation = rcu_dereference(NFS_I(inode)->delegation);
602 if (delegation != NULL) {
603 set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
604 nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
605 }
606 rcu_read_unlock();
607}
608
Trond Myklebusta1d0b5e2012-03-05 19:56:44 -0500609void nfs_remove_bad_delegation(struct inode *inode)
610{
611 struct nfs_delegation *delegation;
612
Trond Myklebust270e2342014-11-10 18:43:56 -0500613 nfs_revoke_delegation(inode);
Trond Myklebustd25be542013-02-05 11:43:28 -0500614 delegation = nfs_inode_detach_delegation(inode);
Trond Myklebusta1d0b5e2012-03-05 19:56:44 -0500615 if (delegation) {
616 nfs_inode_find_state_and_recover(inode, &delegation->stateid);
617 nfs_free_delegation(delegation);
618 }
619}
Andy Adamson9cb81962012-03-07 10:49:41 -0500620EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
Trond Myklebusta1d0b5e2012-03-05 19:56:44 -0500621
Chuck Leverd3978bb2010-12-24 01:33:04 +0000622/**
Trond Myklebust826e0012013-04-03 19:27:52 -0400623 * nfs_expire_unused_delegation_types
Chuck Leverd3978bb2010-12-24 01:33:04 +0000624 * @clp: client to process
625 * @flags: delegation types to expire
626 *
627 */
Trond Myklebust826e0012013-04-03 19:27:52 -0400628void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)
Alexandros Batsakisc79571a2009-12-05 13:20:52 -0500629{
Trond Myklebust826e0012013-04-03 19:27:52 -0400630 nfs_client_mark_return_unused_delegation_types(clp, flags);
Alexandros Batsakisc79571a2009-12-05 13:20:52 -0500631 nfs_delegation_run_state_manager(clp);
632}
633
Chuck Leverd3978bb2010-12-24 01:33:04 +0000634static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
Trond Myklebustb7391f42008-12-23 15:21:52 -0500635{
636 struct nfs_delegation *delegation;
637
Chuck Leverd3978bb2010-12-24 01:33:04 +0000638 list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
Trond Myklebustb7391f42008-12-23 15:21:52 -0500639 if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
640 continue;
Trond Myklebustb7571442013-04-03 14:33:49 -0400641 nfs_mark_return_if_closed_delegation(server, delegation);
Trond Myklebustb7391f42008-12-23 15:21:52 -0500642 }
Trond Myklebustb7391f42008-12-23 15:21:52 -0500643}
644
Chuck Leverd3978bb2010-12-24 01:33:04 +0000645/**
646 * nfs_expire_unreferenced_delegations - Eliminate unused delegations
647 * @clp: nfs_client to process
648 *
649 */
Trond Myklebustb7391f42008-12-23 15:21:52 -0500650void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
651{
Chuck Leverd3978bb2010-12-24 01:33:04 +0000652 struct nfs_server *server;
653
654 rcu_read_lock();
655 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
656 nfs_mark_return_unreferenced_delegations(server);
657 rcu_read_unlock();
658
Trond Myklebustb7391f42008-12-23 15:21:52 -0500659 nfs_delegation_run_state_manager(clp);
660}
661
Chuck Leverd3978bb2010-12-24 01:33:04 +0000662/**
663 * nfs_async_inode_return_delegation - asynchronously return a delegation
664 * @inode: inode to process
Trond Myklebust8e663f02012-03-04 18:13:56 -0500665 * @stateid: state ID information
Chuck Leverd3978bb2010-12-24 01:33:04 +0000666 *
667 * Returns zero on success, or a negative errno value.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 */
Chuck Leverd3978bb2010-12-24 01:33:04 +0000669int nfs_async_inode_return_delegation(struct inode *inode,
670 const nfs4_stateid *stateid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671{
Trond Myklebusted1e62112011-07-25 15:37:29 -0400672 struct nfs_server *server = NFS_SERVER(inode);
673 struct nfs_client *clp = server->nfs_client;
Trond Myklebust6411bd42008-12-23 15:21:51 -0500674 struct nfs_delegation *delegation;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
Trond Myklebustdc327ed2012-05-06 19:46:30 -0400676 filemap_flush(inode->i_mapping);
677
Trond Myklebust6411bd42008-12-23 15:21:51 -0500678 rcu_read_lock();
679 delegation = rcu_dereference(NFS_I(inode)->delegation);
Trond Myklebust40cb6742014-03-02 22:03:12 -0500680 if (delegation == NULL)
681 goto out_enoent;
Alexandros Batsakis25976412009-12-05 13:48:55 -0500682
Trond Myklebust40cb6742014-03-02 22:03:12 -0500683 if (!clp->cl_mvops->match_stateid(&delegation->stateid, stateid))
684 goto out_enoent;
Trond Myklebusted1e62112011-07-25 15:37:29 -0400685 nfs_mark_return_delegation(server, delegation);
Trond Myklebust6411bd42008-12-23 15:21:51 -0500686 rcu_read_unlock();
Chuck Leverd3978bb2010-12-24 01:33:04 +0000687
Trond Myklebust6411bd42008-12-23 15:21:51 -0500688 nfs_delegation_run_state_manager(clp);
689 return 0;
Trond Myklebust40cb6742014-03-02 22:03:12 -0500690out_enoent:
691 rcu_read_unlock();
692 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693}
694
Chuck Leverd3978bb2010-12-24 01:33:04 +0000695static struct inode *
696nfs_delegation_find_inode_server(struct nfs_server *server,
697 const struct nfs_fh *fhandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698{
699 struct nfs_delegation *delegation;
700 struct inode *res = NULL;
Chuck Leverd3978bb2010-12-24 01:33:04 +0000701
702 list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
Trond Myklebust86e89482008-12-23 15:21:39 -0500703 spin_lock(&delegation->lock);
704 if (delegation->inode != NULL &&
705 nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 res = igrab(delegation->inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 }
Trond Myklebust86e89482008-12-23 15:21:39 -0500708 spin_unlock(&delegation->lock);
709 if (res != NULL)
710 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 }
Chuck Leverd3978bb2010-12-24 01:33:04 +0000712 return res;
713}
714
715/**
716 * nfs_delegation_find_inode - retrieve the inode associated with a delegation
717 * @clp: client state handle
718 * @fhandle: filehandle from a delegation recall
719 *
720 * Returns pointer to inode matching "fhandle," or NULL if a matching inode
721 * cannot be found.
722 */
723struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
724 const struct nfs_fh *fhandle)
725{
726 struct nfs_server *server;
727 struct inode *res = NULL;
728
729 rcu_read_lock();
730 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
731 res = nfs_delegation_find_inode_server(server, fhandle);
732 if (res != NULL)
733 break;
734 }
Trond Myklebust8383e462007-07-06 15:12:04 -0400735 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 return res;
737}
738
Chuck Leverd3978bb2010-12-24 01:33:04 +0000739static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
740{
741 struct nfs_delegation *delegation;
742
743 list_for_each_entry_rcu(delegation, &server->delegations, super_list)
744 set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
745}
746
747/**
748 * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed
749 * @clp: nfs_client to process
750 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 */
David Howellsadfa6f92006-08-22 20:06:08 -0400752void nfs_delegation_mark_reclaim(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753{
Chuck Leverd3978bb2010-12-24 01:33:04 +0000754 struct nfs_server *server;
755
Trond Myklebust8383e462007-07-06 15:12:04 -0400756 rcu_read_lock();
Chuck Leverd3978bb2010-12-24 01:33:04 +0000757 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
758 nfs_delegation_mark_reclaim_server(server);
Trond Myklebust8383e462007-07-06 15:12:04 -0400759 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760}
761
Chuck Leverd3978bb2010-12-24 01:33:04 +0000762/**
763 * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done
764 * @clp: nfs_client to process
765 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 */
David Howellsadfa6f92006-08-22 20:06:08 -0400767void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768{
Trond Myklebust8383e462007-07-06 15:12:04 -0400769 struct nfs_delegation *delegation;
Chuck Leverd3978bb2010-12-24 01:33:04 +0000770 struct nfs_server *server;
Trond Myklebust86e89482008-12-23 15:21:39 -0500771 struct inode *inode;
Chuck Leverd3978bb2010-12-24 01:33:04 +0000772
Trond Myklebust8383e462007-07-06 15:12:04 -0400773restart:
774 rcu_read_lock();
Chuck Leverd3978bb2010-12-24 01:33:04 +0000775 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
776 list_for_each_entry_rcu(delegation, &server->delegations,
777 super_list) {
778 if (test_bit(NFS_DELEGATION_NEED_RECLAIM,
779 &delegation->flags) == 0)
780 continue;
781 inode = nfs_delegation_grab_inode(delegation);
782 if (inode == NULL)
783 continue;
784 delegation = nfs_detach_delegation(NFS_I(inode),
Trond Myklebustd25be542013-02-05 11:43:28 -0500785 delegation, server);
Chuck Leverd3978bb2010-12-24 01:33:04 +0000786 rcu_read_unlock();
787
788 if (delegation != NULL)
789 nfs_free_delegation(delegation);
790 iput(inode);
791 goto restart;
792 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 }
Trond Myklebust8383e462007-07-06 15:12:04 -0400794 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795}
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500796
Chuck Leverd3978bb2010-12-24 01:33:04 +0000797/**
798 * nfs_delegations_present - check for existence of delegations
799 * @clp: client state handle
800 *
801 * Returns one if there are any nfs_delegation structures attached
802 * to this nfs_client.
803 */
804int nfs_delegations_present(struct nfs_client *clp)
805{
806 struct nfs_server *server;
807 int ret = 0;
808
809 rcu_read_lock();
810 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
811 if (!list_empty(&server->delegations)) {
812 ret = 1;
813 break;
814 }
815 rcu_read_unlock();
816 return ret;
817}
818
819/**
820 * nfs4_copy_delegation_stateid - Copy inode's state ID information
821 * @dst: stateid data structure to fill in
822 * @inode: inode to check
Trond Myklebust0032a7a2012-03-08 17:16:12 -0500823 * @flags: delegation type requirement
Chuck Leverd3978bb2010-12-24 01:33:04 +0000824 *
Trond Myklebust0032a7a2012-03-08 17:16:12 -0500825 * Returns "true" and fills in "dst->data" * if inode had a delegation,
826 * otherwise "false" is returned.
Chuck Leverd3978bb2010-12-24 01:33:04 +0000827 */
Trond Myklebust0032a7a2012-03-08 17:16:12 -0500828bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode,
829 fmode_t flags)
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500830{
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500831 struct nfs_inode *nfsi = NFS_I(inode);
832 struct nfs_delegation *delegation;
Trond Myklebust0032a7a2012-03-08 17:16:12 -0500833 bool ret;
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500834
Trond Myklebust0032a7a2012-03-08 17:16:12 -0500835 flags &= FMODE_READ|FMODE_WRITE;
Trond Myklebust8383e462007-07-06 15:12:04 -0400836 rcu_read_lock();
837 delegation = rcu_dereference(nfsi->delegation);
Trond Myklebust0032a7a2012-03-08 17:16:12 -0500838 ret = (delegation != NULL && (delegation->type & flags) == flags);
839 if (ret) {
Trond Myklebustf597c532012-03-04 18:13:56 -0500840 nfs4_stateid_copy(dst, &delegation->stateid);
Trond Myklebust0032a7a2012-03-08 17:16:12 -0500841 nfs_mark_delegation_referenced(delegation);
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500842 }
Trond Myklebust8383e462007-07-06 15:12:04 -0400843 rcu_read_unlock();
844 return ret;
Trond Myklebust3e4f6292006-03-20 13:44:46 -0500845}