xref: /openbmc/linux/fs/nfs/delegation.c (revision 6c342655)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/fs/nfs/delegation.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 2004 Trond Myklebust
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * NFS file delegation management
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds #include <linux/completion.h>
1058d9714aSTrond Myklebust #include <linux/kthread.h>
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/sched.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/spinlock.h>
151eb5d98fSJeff Layton #include <linux/iversion.h>
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds #include <linux/nfs4.h>
181da177e4SLinus Torvalds #include <linux/nfs_fs.h>
191da177e4SLinus Torvalds #include <linux/nfs_xdr.h>
201da177e4SLinus Torvalds 
214ce79717STrond Myklebust #include "nfs4_fs.h"
22c01d3645STrond Myklebust #include "nfs4session.h"
231da177e4SLinus Torvalds #include "delegation.h"
2424c8dbbbSDavid Howells #include "internal.h"
25ca8acf8dSTrond Myklebust #include "nfs4trace.h"
261da177e4SLinus Torvalds 
27905f8d16STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation)
28905f8d16STrond Myklebust {
29e00b8a24STrond Myklebust 	if (delegation->cred) {
30e00b8a24STrond Myklebust 		put_rpccred(delegation->cred);
31e00b8a24STrond Myklebust 		delegation->cred = NULL;
32e00b8a24STrond Myklebust 	}
3326f04ddeSLai Jiangshan 	kfree_rcu(delegation, rcu);
348383e460STrond Myklebust }
358383e460STrond Myklebust 
36d3978bb3SChuck Lever /**
37d3978bb3SChuck Lever  * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
38d3978bb3SChuck Lever  * @delegation: delegation to process
39d3978bb3SChuck Lever  *
40d3978bb3SChuck Lever  */
41b7391f44STrond Myklebust void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
42b7391f44STrond Myklebust {
43b7391f44STrond Myklebust 	set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
44b7391f44STrond Myklebust }
45b7391f44STrond Myklebust 
46aa05c87fSTrond Myklebust static bool
47aa05c87fSTrond Myklebust nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
48aa05c87fSTrond Myklebust 		fmode_t flags)
49aa05c87fSTrond Myklebust {
50aa05c87fSTrond Myklebust 	if (delegation != NULL && (delegation->type & flags) == flags &&
51aa05c87fSTrond Myklebust 	    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
52aa05c87fSTrond Myklebust 	    !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
53aa05c87fSTrond Myklebust 		return true;
54aa05c87fSTrond Myklebust 	return false;
55aa05c87fSTrond Myklebust }
56aa05c87fSTrond Myklebust 
5715bb3afeSPeng Tao static int
5815bb3afeSPeng Tao nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
59b7391f44STrond Myklebust {
60b7391f44STrond Myklebust 	struct nfs_delegation *delegation;
61b7391f44STrond Myklebust 	int ret = 0;
62b7391f44STrond Myklebust 
63b7391f44STrond Myklebust 	flags &= FMODE_READ|FMODE_WRITE;
64b7391f44STrond Myklebust 	rcu_read_lock();
65b7391f44STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
66aa05c87fSTrond Myklebust 	if (nfs4_is_valid_delegation(delegation, flags)) {
6715bb3afeSPeng Tao 		if (mark)
68b7391f44STrond Myklebust 			nfs_mark_delegation_referenced(delegation);
69b7391f44STrond Myklebust 		ret = 1;
70b7391f44STrond Myklebust 	}
71b7391f44STrond Myklebust 	rcu_read_unlock();
72b7391f44STrond Myklebust 	return ret;
73b7391f44STrond Myklebust }
7415bb3afeSPeng Tao /**
7515bb3afeSPeng Tao  * nfs_have_delegation - check if inode has a delegation, mark it
7615bb3afeSPeng Tao  * NFS_DELEGATION_REFERENCED if there is one.
7715bb3afeSPeng Tao  * @inode: inode to check
7815bb3afeSPeng Tao  * @flags: delegation types to check for
7915bb3afeSPeng Tao  *
8015bb3afeSPeng Tao  * Returns one if inode has the indicated delegation, otherwise zero.
8115bb3afeSPeng Tao  */
8215bb3afeSPeng Tao int nfs4_have_delegation(struct inode *inode, fmode_t flags)
8315bb3afeSPeng Tao {
8415bb3afeSPeng Tao 	return nfs4_do_check_delegation(inode, flags, true);
8515bb3afeSPeng Tao }
8615bb3afeSPeng Tao 
8715bb3afeSPeng Tao /*
8815bb3afeSPeng Tao  * nfs4_check_delegation - check if inode has a delegation, do not mark
8915bb3afeSPeng Tao  * NFS_DELEGATION_REFERENCED if it has one.
9015bb3afeSPeng Tao  */
9115bb3afeSPeng Tao int nfs4_check_delegation(struct inode *inode, fmode_t flags)
9215bb3afeSPeng Tao {
9315bb3afeSPeng Tao 	return nfs4_do_check_delegation(inode, flags, false);
9415bb3afeSPeng Tao }
95b7391f44STrond Myklebust 
96db4f2e63STrond Myklebust static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
97888e694cSTrond Myklebust {
98888e694cSTrond Myklebust 	struct inode *inode = state->inode;
99888e694cSTrond Myklebust 	struct file_lock *fl;
100bd61e0a9SJeff Layton 	struct file_lock_context *flctx = inode->i_flctx;
101bd61e0a9SJeff Layton 	struct list_head *list;
102d5122201STrond Myklebust 	int status = 0;
103888e694cSTrond Myklebust 
104bd61e0a9SJeff Layton 	if (flctx == NULL)
10565b62a29STrond Myklebust 		goto out;
106314d7cc0SJeff Layton 
107bd61e0a9SJeff Layton 	list = &flctx->flc_posix;
1086109c850SJeff Layton 	spin_lock(&flctx->flc_lock);
109bd61e0a9SJeff Layton restart:
110bd61e0a9SJeff Layton 	list_for_each_entry(fl, list, fl_list) {
111cd3758e3STrond Myklebust 		if (nfs_file_open_context(fl->fl_file) != ctx)
112888e694cSTrond Myklebust 			continue;
1136109c850SJeff Layton 		spin_unlock(&flctx->flc_lock);
114db4f2e63STrond Myklebust 		status = nfs4_lock_delegation_recall(fl, state, stateid);
115d5122201STrond Myklebust 		if (status < 0)
1163f09df70STrond Myklebust 			goto out;
1176109c850SJeff Layton 		spin_lock(&flctx->flc_lock);
118888e694cSTrond Myklebust 	}
119bd61e0a9SJeff Layton 	if (list == &flctx->flc_posix) {
120bd61e0a9SJeff Layton 		list = &flctx->flc_flock;
121bd61e0a9SJeff Layton 		goto restart;
1225263e31eSJeff Layton 	}
1236109c850SJeff Layton 	spin_unlock(&flctx->flc_lock);
1243f09df70STrond Myklebust out:
125888e694cSTrond Myklebust 	return status;
126888e694cSTrond Myklebust }
127888e694cSTrond Myklebust 
12824311f88STrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode,
12924311f88STrond Myklebust 		const nfs4_stateid *stateid, fmode_t type)
1301da177e4SLinus Torvalds {
1311da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
1321da177e4SLinus Torvalds 	struct nfs_open_context *ctx;
133d25be546STrond Myklebust 	struct nfs4_state_owner *sp;
1341da177e4SLinus Torvalds 	struct nfs4_state *state;
135d25be546STrond Myklebust 	unsigned int seq;
136888e694cSTrond Myklebust 	int err;
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds again:
1391da177e4SLinus Torvalds 	spin_lock(&inode->i_lock);
1401da177e4SLinus Torvalds 	list_for_each_entry(ctx, &nfsi->open_files, list) {
1411da177e4SLinus Torvalds 		state = ctx->state;
1421da177e4SLinus Torvalds 		if (state == NULL)
1431da177e4SLinus Torvalds 			continue;
1441da177e4SLinus Torvalds 		if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
1451da177e4SLinus Torvalds 			continue;
146f8ebf7a8STrond Myklebust 		if (!nfs4_valid_open_stateid(state))
147f8ebf7a8STrond Myklebust 			continue;
148f597c537STrond Myklebust 		if (!nfs4_stateid_match(&state->stateid, stateid))
14990163027STrond Myklebust 			continue;
1501da177e4SLinus Torvalds 		get_nfs_open_context(ctx);
1511da177e4SLinus Torvalds 		spin_unlock(&inode->i_lock);
152d25be546STrond Myklebust 		sp = state->owner;
15365b62a29STrond Myklebust 		/* Block nfs4_proc_unlck */
15465b62a29STrond Myklebust 		mutex_lock(&sp->so_delegreturn_mutex);
155d25be546STrond Myklebust 		seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
15624311f88STrond Myklebust 		err = nfs4_open_delegation_recall(ctx, state, stateid, type);
157d25be546STrond Myklebust 		if (!err)
158db4f2e63STrond Myklebust 			err = nfs_delegation_claim_locks(ctx, state, stateid);
159d25be546STrond Myklebust 		if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
160d25be546STrond Myklebust 			err = -EAGAIN;
16165b62a29STrond Myklebust 		mutex_unlock(&sp->so_delegreturn_mutex);
1621da177e4SLinus Torvalds 		put_nfs_open_context(ctx);
163888e694cSTrond Myklebust 		if (err != 0)
164d18cc1fdSTrond Myklebust 			return err;
1651da177e4SLinus Torvalds 		goto again;
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 	spin_unlock(&inode->i_lock);
168d18cc1fdSTrond Myklebust 	return 0;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
171d3978bb3SChuck Lever /**
172d3978bb3SChuck Lever  * nfs_inode_reclaim_delegation - process a delegation reclaim request
173d3978bb3SChuck Lever  * @inode: inode to process
174d3978bb3SChuck Lever  * @cred: credential to use for request
17535156bffSTrond Myklebust  * @type: delegation type
17635156bffSTrond Myklebust  * @stateid: delegation stateid
17735156bffSTrond Myklebust  * @pagemod_limit: write delegation "space_limit"
178d3978bb3SChuck Lever  *
1791da177e4SLinus Torvalds  */
180d3978bb3SChuck Lever void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
18135156bffSTrond Myklebust 				  fmode_t type,
18235156bffSTrond Myklebust 				  const nfs4_stateid *stateid,
18335156bffSTrond Myklebust 				  unsigned long pagemod_limit)
1841da177e4SLinus Torvalds {
1858f649c37STrond Myklebust 	struct nfs_delegation *delegation;
1868f649c37STrond Myklebust 	struct rpc_cred *oldcred = NULL;
1871da177e4SLinus Torvalds 
1888f649c37STrond Myklebust 	rcu_read_lock();
1898f649c37STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
1908f649c37STrond Myklebust 	if (delegation != NULL) {
1918f649c37STrond Myklebust 		spin_lock(&delegation->lock);
1928f649c37STrond Myklebust 		if (delegation->inode != NULL) {
19335156bffSTrond Myklebust 			nfs4_stateid_copy(&delegation->stateid, stateid);
19435156bffSTrond Myklebust 			delegation->type = type;
19535156bffSTrond Myklebust 			delegation->pagemod_limit = pagemod_limit;
19605c88babSTrond Myklebust 			oldcred = delegation->cred;
1971da177e4SLinus Torvalds 			delegation->cred = get_rpccred(cred);
1988f649c37STrond Myklebust 			clear_bit(NFS_DELEGATION_NEED_RECLAIM,
1998f649c37STrond Myklebust 				  &delegation->flags);
2008f649c37STrond Myklebust 			spin_unlock(&delegation->lock);
2018f649c37STrond Myklebust 			rcu_read_unlock();
2027c0af9ffSTrond Myklebust 			put_rpccred(oldcred);
20335156bffSTrond Myklebust 			trace_nfs4_reclaim_delegation(inode, type);
204b1a318deSTrond Myklebust 			return;
205b1a318deSTrond Myklebust 		}
2068f649c37STrond Myklebust 		/* We appear to have raced with a delegation return. */
2078f649c37STrond Myklebust 		spin_unlock(&delegation->lock);
208b1a318deSTrond Myklebust 	}
2098f649c37STrond Myklebust 	rcu_read_unlock();
21035156bffSTrond Myklebust 	nfs_inode_set_delegation(inode, cred, type, stateid, pagemod_limit);
2118f649c37STrond Myklebust }
2121da177e4SLinus Torvalds 
21357bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
21457bfa891STrond Myklebust {
21557bfa891STrond Myklebust 	int res = 0;
21657bfa891STrond Myklebust 
217869f9dfaSTrond Myklebust 	if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
218869f9dfaSTrond Myklebust 		res = nfs4_proc_delegreturn(inode,
219869f9dfaSTrond Myklebust 				delegation->cred,
220869f9dfaSTrond Myklebust 				&delegation->stateid,
221869f9dfaSTrond Myklebust 				issync);
22257bfa891STrond Myklebust 	nfs_free_delegation(delegation);
22357bfa891STrond Myklebust 	return res;
22457bfa891STrond Myklebust }
22557bfa891STrond Myklebust 
22686e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
22786e89489STrond Myklebust {
22886e89489STrond Myklebust 	struct inode *inode = NULL;
22986e89489STrond Myklebust 
23086e89489STrond Myklebust 	spin_lock(&delegation->lock);
23186e89489STrond Myklebust 	if (delegation->inode != NULL)
23286e89489STrond Myklebust 		inode = igrab(delegation->inode);
23386e89489STrond Myklebust 	spin_unlock(&delegation->lock);
23486e89489STrond Myklebust 	return inode;
23586e89489STrond Myklebust }
23686e89489STrond Myklebust 
237dda4b225SChuck Lever static struct nfs_delegation *
238d25be546STrond Myklebust nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
23957bfa891STrond Myklebust {
240d25be546STrond Myklebust 	struct nfs_delegation *ret = NULL;
241d25be546STrond Myklebust 	struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
24257bfa891STrond Myklebust 
24357bfa891STrond Myklebust 	if (delegation == NULL)
244d25be546STrond Myklebust 		goto out;
245d25be546STrond Myklebust 	spin_lock(&delegation->lock);
246d25be546STrond Myklebust 	if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
247d25be546STrond Myklebust 		ret = delegation;
248d25be546STrond Myklebust 	spin_unlock(&delegation->lock);
249d25be546STrond Myklebust out:
250d25be546STrond Myklebust 	return ret;
251d25be546STrond Myklebust }
252d25be546STrond Myklebust 
253d25be546STrond Myklebust static struct nfs_delegation *
254d25be546STrond Myklebust nfs_start_delegation_return(struct nfs_inode *nfsi)
255d25be546STrond Myklebust {
256d25be546STrond Myklebust 	struct nfs_delegation *delegation;
257d25be546STrond Myklebust 
258d25be546STrond Myklebust 	rcu_read_lock();
259d25be546STrond Myklebust 	delegation = nfs_start_delegation_return_locked(nfsi);
260d25be546STrond Myklebust 	rcu_read_unlock();
261d25be546STrond Myklebust 	return delegation;
262d25be546STrond Myklebust }
263d25be546STrond Myklebust 
264d25be546STrond Myklebust static void
265d25be546STrond Myklebust nfs_abort_delegation_return(struct nfs_delegation *delegation,
266d25be546STrond Myklebust 		struct nfs_client *clp)
267d25be546STrond Myklebust {
268dda4b225SChuck Lever 
26934310430STrond Myklebust 	spin_lock(&delegation->lock);
270d25be546STrond Myklebust 	clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
271d25be546STrond Myklebust 	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
272d25be546STrond Myklebust 	spin_unlock(&delegation->lock);
273d25be546STrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
274d25be546STrond Myklebust }
275d25be546STrond Myklebust 
276d25be546STrond Myklebust static struct nfs_delegation *
277d25be546STrond Myklebust nfs_detach_delegation_locked(struct nfs_inode *nfsi,
278d25be546STrond Myklebust 		struct nfs_delegation *delegation,
279d25be546STrond Myklebust 		struct nfs_client *clp)
280d25be546STrond Myklebust {
281d25be546STrond Myklebust 	struct nfs_delegation *deleg_cur =
282d25be546STrond Myklebust 		rcu_dereference_protected(nfsi->delegation,
283d25be546STrond Myklebust 				lockdep_is_held(&clp->cl_lock));
284d25be546STrond Myklebust 
285d25be546STrond Myklebust 	if (deleg_cur == NULL || delegation != deleg_cur)
286d25be546STrond Myklebust 		return NULL;
287d25be546STrond Myklebust 
288d25be546STrond Myklebust 	spin_lock(&delegation->lock);
289d25be546STrond Myklebust 	set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
29057bfa891STrond Myklebust 	list_del_rcu(&delegation->super_list);
29186e89489STrond Myklebust 	delegation->inode = NULL;
29257bfa891STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, NULL);
29334310430STrond Myklebust 	spin_unlock(&delegation->lock);
29457bfa891STrond Myklebust 	return delegation;
29557bfa891STrond Myklebust }
29657bfa891STrond Myklebust 
297dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
298d25be546STrond Myklebust 		struct nfs_delegation *delegation,
299d3978bb3SChuck Lever 		struct nfs_server *server)
300dda4b225SChuck Lever {
301d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
302dda4b225SChuck Lever 
303dda4b225SChuck Lever 	spin_lock(&clp->cl_lock);
304d25be546STrond Myklebust 	delegation = nfs_detach_delegation_locked(nfsi, delegation, clp);
305dda4b225SChuck Lever 	spin_unlock(&clp->cl_lock);
306dda4b225SChuck Lever 	return delegation;
307dda4b225SChuck Lever }
308dda4b225SChuck Lever 
309d25be546STrond Myklebust static struct nfs_delegation *
310d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode)
311d25be546STrond Myklebust {
312d25be546STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
313d25be546STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
314d25be546STrond Myklebust 	struct nfs_delegation *delegation;
315d25be546STrond Myklebust 
316d25be546STrond Myklebust 	delegation = nfs_start_delegation_return(nfsi);
317d25be546STrond Myklebust 	if (delegation == NULL)
318d25be546STrond Myklebust 		return NULL;
319d25be546STrond Myklebust 	return nfs_detach_delegation(nfsi, delegation, server);
320d25be546STrond Myklebust }
321d25be546STrond Myklebust 
322cf6726e2STrond Myklebust static void
323cf6726e2STrond Myklebust nfs_update_inplace_delegation(struct nfs_delegation *delegation,
324cf6726e2STrond Myklebust 		const struct nfs_delegation *update)
325cf6726e2STrond Myklebust {
326cf6726e2STrond Myklebust 	if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) {
327cf6726e2STrond Myklebust 		delegation->stateid.seqid = update->stateid.seqid;
328cf6726e2STrond Myklebust 		smp_wmb();
329cf6726e2STrond Myklebust 		delegation->type = update->type;
330cf6726e2STrond Myklebust 	}
331cf6726e2STrond Myklebust }
332cf6726e2STrond Myklebust 
333d3978bb3SChuck Lever /**
334d3978bb3SChuck Lever  * nfs_inode_set_delegation - set up a delegation on an inode
335d3978bb3SChuck Lever  * @inode: inode to which delegation applies
336d3978bb3SChuck Lever  * @cred: cred to use for subsequent delegation processing
33735156bffSTrond Myklebust  * @type: delegation type
33835156bffSTrond Myklebust  * @stateid: delegation stateid
33935156bffSTrond Myklebust  * @pagemod_limit: write delegation "space_limit"
340d3978bb3SChuck Lever  *
341d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
3421da177e4SLinus Torvalds  */
34335156bffSTrond Myklebust int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred,
34435156bffSTrond Myklebust 				  fmode_t type,
34535156bffSTrond Myklebust 				  const nfs4_stateid *stateid,
34635156bffSTrond Myklebust 				  unsigned long pagemod_limit)
3471da177e4SLinus Torvalds {
348d3978bb3SChuck Lever 	struct nfs_server *server = NFS_SERVER(inode);
349d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
3501da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
35117d2c0a0SDavid Howells 	struct nfs_delegation *delegation, *old_delegation;
35257bfa891STrond Myklebust 	struct nfs_delegation *freeme = NULL;
3531da177e4SLinus Torvalds 	int status = 0;
3541da177e4SLinus Torvalds 
3558535b2beSTrond Myklebust 	delegation = kmalloc(sizeof(*delegation), GFP_NOFS);
3561da177e4SLinus Torvalds 	if (delegation == NULL)
3571da177e4SLinus Torvalds 		return -ENOMEM;
35835156bffSTrond Myklebust 	nfs4_stateid_copy(&delegation->stateid, stateid);
35935156bffSTrond Myklebust 	delegation->type = type;
36035156bffSTrond Myklebust 	delegation->pagemod_limit = pagemod_limit;
3611eb5d98fSJeff Layton 	delegation->change_attr = inode_peek_iversion_raw(inode);
3621da177e4SLinus Torvalds 	delegation->cred = get_rpccred(cred);
3631da177e4SLinus Torvalds 	delegation->inode = inode;
364b7391f44STrond Myklebust 	delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
36534310430STrond Myklebust 	spin_lock_init(&delegation->lock);
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
36817d2c0a0SDavid Howells 	old_delegation = rcu_dereference_protected(nfsi->delegation,
36917d2c0a0SDavid Howells 					lockdep_is_held(&clp->cl_lock));
37017d2c0a0SDavid Howells 	if (old_delegation != NULL) {
371cf6726e2STrond Myklebust 		/* Is this an update of the existing delegation? */
372cf6726e2STrond Myklebust 		if (nfs4_stateid_match_other(&old_delegation->stateid,
373cf6726e2STrond Myklebust 					&delegation->stateid)) {
374cf6726e2STrond Myklebust 			nfs_update_inplace_delegation(old_delegation,
375cf6726e2STrond Myklebust 					delegation);
37657bfa891STrond Myklebust 			goto out;
37757bfa891STrond Myklebust 		}
37857bfa891STrond Myklebust 		/*
37957bfa891STrond Myklebust 		 * Deal with broken servers that hand out two
38057bfa891STrond Myklebust 		 * delegations for the same file.
38117280175STrond Myklebust 		 * Allow for upgrades to a WRITE delegation, but
38217280175STrond Myklebust 		 * nothing else.
38357bfa891STrond Myklebust 		 */
38457bfa891STrond Myklebust 		dfprintk(FILE, "%s: server %s handed out "
38557bfa891STrond Myklebust 				"a duplicate delegation!\n",
3863110ff80SHarvey Harrison 				__func__, clp->cl_hostname);
38717280175STrond Myklebust 		if (delegation->type == old_delegation->type ||
38817280175STrond Myklebust 		    !(delegation->type & FMODE_WRITE)) {
38957bfa891STrond Myklebust 			freeme = delegation;
39057bfa891STrond Myklebust 			delegation = NULL;
39157bfa891STrond Myklebust 			goto out;
39257bfa891STrond Myklebust 		}
393ade04647STrond Myklebust 		if (test_and_set_bit(NFS_DELEGATION_RETURNING,
394ade04647STrond Myklebust 					&old_delegation->flags))
395ade04647STrond Myklebust 			goto out;
396d25be546STrond Myklebust 		freeme = nfs_detach_delegation_locked(nfsi,
397d25be546STrond Myklebust 				old_delegation, clp);
398d25be546STrond Myklebust 		if (freeme == NULL)
399d25be546STrond Myklebust 			goto out;
40057bfa891STrond Myklebust 	}
40138942ba2STrond Myklebust 	list_add_tail_rcu(&delegation->super_list, &server->delegations);
4028383e460STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, delegation);
4031da177e4SLinus Torvalds 	delegation = NULL;
404412c77ceSTrond Myklebust 
40535156bffSTrond Myklebust 	trace_nfs4_set_delegation(inode, type);
406412c77ceSTrond Myklebust 
40797c2c17aSTrond Myklebust 	spin_lock(&inode->i_lock);
40897c2c17aSTrond Myklebust 	if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME))
40997c2c17aSTrond Myklebust 		NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED;
41097c2c17aSTrond Myklebust 	spin_unlock(&inode->i_lock);
41157bfa891STrond Myklebust out:
4121da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
413603c83daSTrond Myklebust 	if (delegation != NULL)
414603c83daSTrond Myklebust 		nfs_free_delegation(delegation);
41557bfa891STrond Myklebust 	if (freeme != NULL)
41657bfa891STrond Myklebust 		nfs_do_return_delegation(inode, freeme, 0);
4171da177e4SLinus Torvalds 	return status;
4181da177e4SLinus Torvalds }
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds /*
4211da177e4SLinus Torvalds  * Basic procedure for returning a delegation to the server
4221da177e4SLinus Torvalds  */
423d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
4241da177e4SLinus Torvalds {
425d25be546STrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
4261da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
427869f9dfaSTrond Myklebust 	int err = 0;
4281da177e4SLinus Torvalds 
429d25be546STrond Myklebust 	if (delegation == NULL)
430d25be546STrond Myklebust 		return 0;
431d25be546STrond Myklebust 	do {
432869f9dfaSTrond Myklebust 		if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
433869f9dfaSTrond Myklebust 			break;
43424311f88STrond Myklebust 		err = nfs_delegation_claim_opens(inode, &delegation->stateid,
43524311f88STrond Myklebust 				delegation->type);
436d25be546STrond Myklebust 		if (!issync || err != -EAGAIN)
437d25be546STrond Myklebust 			break;
438d25be546STrond Myklebust 		/*
439d25be546STrond Myklebust 		 * Guard against state recovery
440d25be546STrond Myklebust 		 */
441d25be546STrond Myklebust 		err = nfs4_wait_clnt_recover(clp);
442d25be546STrond Myklebust 	} while (err == 0);
443d25be546STrond Myklebust 
444d25be546STrond Myklebust 	if (err) {
445d25be546STrond Myklebust 		nfs_abort_delegation_return(delegation, clp);
446d25be546STrond Myklebust 		goto out;
447d25be546STrond Myklebust 	}
448d25be546STrond Myklebust 	if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode)))
449d18cc1fdSTrond Myklebust 		goto out;
4501da177e4SLinus Torvalds 
451d18cc1fdSTrond Myklebust 	err = nfs_do_return_delegation(inode, delegation, issync);
452d18cc1fdSTrond Myklebust out:
453d18cc1fdSTrond Myklebust 	return err;
45490163027STrond Myklebust }
45590163027STrond Myklebust 
456b757144fSTrond Myklebust static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
457b757144fSTrond Myklebust {
458b757144fSTrond Myklebust 	bool ret = false;
459b757144fSTrond Myklebust 
460ec3ca4e5STrond Myklebust 	if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
461ec3ca4e5STrond Myklebust 		goto out;
462b757144fSTrond Myklebust 	if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
463b757144fSTrond Myklebust 		ret = true;
464b757144fSTrond Myklebust 	if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) {
465b757144fSTrond Myklebust 		struct inode *inode;
466b757144fSTrond Myklebust 
467b757144fSTrond Myklebust 		spin_lock(&delegation->lock);
468b757144fSTrond Myklebust 		inode = delegation->inode;
469b757144fSTrond Myklebust 		if (inode && list_empty(&NFS_I(inode)->open_files))
470b757144fSTrond Myklebust 			ret = true;
471b757144fSTrond Myklebust 		spin_unlock(&delegation->lock);
472b757144fSTrond Myklebust 	}
473ec3ca4e5STrond Myklebust out:
474b757144fSTrond Myklebust 	return ret;
475b757144fSTrond Myklebust }
476b757144fSTrond Myklebust 
477d3978bb3SChuck Lever /**
478d3978bb3SChuck Lever  * nfs_client_return_marked_delegations - return previously marked delegations
479d3978bb3SChuck Lever  * @clp: nfs_client to process
480d3978bb3SChuck Lever  *
481dc327ed4STrond Myklebust  * Note that this function is designed to be called by the state
482dc327ed4STrond Myklebust  * manager thread. For this reason, it cannot flush the dirty data,
483dc327ed4STrond Myklebust  * since that could deadlock in case of a state recovery error.
484dc327ed4STrond Myklebust  *
485d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
486515d8611STrond Myklebust  */
487d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp)
488515d8611STrond Myklebust {
489515d8611STrond Myklebust 	struct nfs_delegation *delegation;
490e04bbf6bSNeilBrown 	struct nfs_delegation *prev;
491d3978bb3SChuck Lever 	struct nfs_server *server;
492515d8611STrond Myklebust 	struct inode *inode;
493e04bbf6bSNeilBrown 	struct inode *place_holder = NULL;
494e04bbf6bSNeilBrown 	struct nfs_delegation *place_holder_deleg = NULL;
495d18cc1fdSTrond Myklebust 	int err = 0;
496515d8611STrond Myklebust 
497515d8611STrond Myklebust restart:
498e04bbf6bSNeilBrown 	/*
499e04bbf6bSNeilBrown 	 * To avoid quadratic looping we hold a reference
500e04bbf6bSNeilBrown 	 * to an inode place_holder.  Each time we restart, we
501e04bbf6bSNeilBrown 	 * list nfs_servers from the server of that inode, and
502e04bbf6bSNeilBrown 	 * delegation in the server from the delegations of that
503e04bbf6bSNeilBrown 	 * inode.
504e04bbf6bSNeilBrown 	 * prev is an RCU-protected pointer to a delegation which
505e04bbf6bSNeilBrown 	 * wasn't marked for return and might be a good choice for
506e04bbf6bSNeilBrown 	 * the next place_holder.
507e04bbf6bSNeilBrown 	 */
508515d8611STrond Myklebust 	rcu_read_lock();
509e04bbf6bSNeilBrown 	prev = NULL;
510e04bbf6bSNeilBrown 	if (place_holder)
511e04bbf6bSNeilBrown 		server = NFS_SERVER(place_holder);
512e04bbf6bSNeilBrown 	else
513e04bbf6bSNeilBrown 		server = list_entry_rcu(clp->cl_superblocks.next,
514e04bbf6bSNeilBrown 					struct nfs_server, client_link);
515e04bbf6bSNeilBrown 	list_for_each_entry_from_rcu(server, &clp->cl_superblocks, client_link) {
516e04bbf6bSNeilBrown 		delegation = NULL;
517e04bbf6bSNeilBrown 		if (place_holder && server == NFS_SERVER(place_holder))
518e04bbf6bSNeilBrown 			delegation = rcu_dereference(NFS_I(place_holder)->delegation);
519e04bbf6bSNeilBrown 		if (!delegation || delegation != place_holder_deleg)
520e04bbf6bSNeilBrown 			delegation = list_entry_rcu(server->delegations.next,
521e04bbf6bSNeilBrown 						    struct nfs_delegation, super_list);
522e04bbf6bSNeilBrown 		list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) {
523e04bbf6bSNeilBrown 			struct inode *to_put = NULL;
524e04bbf6bSNeilBrown 
525e04bbf6bSNeilBrown 			if (!nfs_delegation_need_return(delegation)) {
526e04bbf6bSNeilBrown 				prev = delegation;
527515d8611STrond Myklebust 				continue;
528e04bbf6bSNeilBrown 			}
5299f0f8e12STrond Myklebust 			if (!nfs_sb_active(server->super))
530f3893491SNeilBrown 				break; /* continue in outer loop */
531e04bbf6bSNeilBrown 
532e04bbf6bSNeilBrown 			if (prev) {
533e04bbf6bSNeilBrown 				struct inode *tmp;
534e04bbf6bSNeilBrown 
535e04bbf6bSNeilBrown 				tmp = nfs_delegation_grab_inode(prev);
536e04bbf6bSNeilBrown 				if (tmp) {
537e04bbf6bSNeilBrown 					to_put = place_holder;
538e04bbf6bSNeilBrown 					place_holder = tmp;
539e04bbf6bSNeilBrown 					place_holder_deleg = prev;
540e04bbf6bSNeilBrown 				}
541e04bbf6bSNeilBrown 			}
542e04bbf6bSNeilBrown 
5439f0f8e12STrond Myklebust 			inode = nfs_delegation_grab_inode(delegation);
5449f0f8e12STrond Myklebust 			if (inode == NULL) {
5459f0f8e12STrond Myklebust 				rcu_read_unlock();
546e04bbf6bSNeilBrown 				if (to_put)
547e04bbf6bSNeilBrown 					iput(to_put);
5489f0f8e12STrond Myklebust 				nfs_sb_deactive(server->super);
5499f0f8e12STrond Myklebust 				goto restart;
5509f0f8e12STrond Myklebust 			}
551d25be546STrond Myklebust 			delegation = nfs_start_delegation_return_locked(NFS_I(inode));
552515d8611STrond Myklebust 			rcu_read_unlock();
553d3978bb3SChuck Lever 
554e04bbf6bSNeilBrown 			if (to_put)
555e04bbf6bSNeilBrown 				iput(to_put);
556e04bbf6bSNeilBrown 
557d25be546STrond Myklebust 			err = nfs_end_delegation_return(inode, delegation, 0);
558515d8611STrond Myklebust 			iput(inode);
5599f0f8e12STrond Myklebust 			nfs_sb_deactive(server->super);
5603ca951b6SNeilBrown 			cond_resched();
561d18cc1fdSTrond Myklebust 			if (!err)
562515d8611STrond Myklebust 				goto restart;
563d18cc1fdSTrond Myklebust 			set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
564e04bbf6bSNeilBrown 			if (place_holder)
565e04bbf6bSNeilBrown 				iput(place_holder);
566d18cc1fdSTrond Myklebust 			return err;
567515d8611STrond Myklebust 		}
568d3978bb3SChuck Lever 	}
569515d8611STrond Myklebust 	rcu_read_unlock();
570e04bbf6bSNeilBrown 	if (place_holder)
571e04bbf6bSNeilBrown 		iput(place_holder);
572d18cc1fdSTrond Myklebust 	return 0;
573515d8611STrond Myklebust }
574515d8611STrond Myklebust 
575d3978bb3SChuck Lever /**
576d3978bb3SChuck Lever  * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens
577d3978bb3SChuck Lever  * @inode: inode to process
578d3978bb3SChuck Lever  *
579d3978bb3SChuck Lever  * Does not protect against delegation reclaims, therefore really only safe
580d3978bb3SChuck Lever  * to be called from nfs4_clear_inode().
581e6f81075STrond Myklebust  */
582e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode)
583e6f81075STrond Myklebust {
584e6f81075STrond Myklebust 	struct nfs_delegation *delegation;
585e6f81075STrond Myklebust 
586d25be546STrond Myklebust 	delegation = nfs_inode_detach_delegation(inode);
587e6f81075STrond Myklebust 	if (delegation != NULL)
5885fcdfaccSTrond Myklebust 		nfs_do_return_delegation(inode, delegation, 1);
589e6f81075STrond Myklebust }
590e6f81075STrond Myklebust 
591d3978bb3SChuck Lever /**
592d3978bb3SChuck Lever  * nfs_inode_return_delegation - synchronously return a delegation
593d3978bb3SChuck Lever  * @inode: inode to process
594d3978bb3SChuck Lever  *
595c57d1bc5STrond Myklebust  * This routine will always flush any dirty data to disk on the
596c57d1bc5STrond Myklebust  * assumption that if we need to return the delegation, then
597c57d1bc5STrond Myklebust  * we should stop caching.
598c57d1bc5STrond Myklebust  *
599d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
600d3978bb3SChuck Lever  */
60157ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode)
60290163027STrond Myklebust {
60390163027STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
60490163027STrond Myklebust 	struct nfs_delegation *delegation;
60590163027STrond Myklebust 	int err = 0;
60690163027STrond Myklebust 
607c57d1bc5STrond Myklebust 	nfs_wb_all(inode);
608d25be546STrond Myklebust 	delegation = nfs_start_delegation_return(nfsi);
609d25be546STrond Myklebust 	if (delegation != NULL)
610d25be546STrond Myklebust 		err = nfs_end_delegation_return(inode, delegation, 1);
61190163027STrond Myklebust 	return err;
6121da177e4SLinus Torvalds }
6131da177e4SLinus Torvalds 
614c01d3645STrond Myklebust /**
615c01d3645STrond Myklebust  * nfs4_inode_make_writeable
616c01d3645STrond Myklebust  * @inode: pointer to inode
617c01d3645STrond Myklebust  *
618c01d3645STrond Myklebust  * Make the inode writeable by returning the delegation if necessary
619c01d3645STrond Myklebust  *
620c01d3645STrond Myklebust  * Returns zero on success, or a negative errno value.
621c01d3645STrond Myklebust  */
622c01d3645STrond Myklebust int nfs4_inode_make_writeable(struct inode *inode)
623c01d3645STrond Myklebust {
624c01d3645STrond Myklebust 	if (!nfs4_has_session(NFS_SERVER(inode)->nfs_client) ||
625c01d3645STrond Myklebust 	    !nfs4_check_delegation(inode, FMODE_WRITE))
626c01d3645STrond Myklebust 		return nfs4_inode_return_delegation(inode);
627c01d3645STrond Myklebust 	return 0;
628c01d3645STrond Myklebust }
629c01d3645STrond Myklebust 
630b757144fSTrond Myklebust static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
631b757144fSTrond Myklebust 		struct nfs_delegation *delegation)
632b757144fSTrond Myklebust {
633b757144fSTrond Myklebust 	set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
634b757144fSTrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
635b757144fSTrond Myklebust }
636b757144fSTrond Myklebust 
637ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server,
638ed1e6211STrond Myklebust 		struct nfs_delegation *delegation)
6396411bd4aSTrond Myklebust {
6406411bd4aSTrond Myklebust 	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
641ed1e6211STrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
6426411bd4aSTrond Myklebust }
6436411bd4aSTrond Myklebust 
6445c31e236STrond Myklebust static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
6455c31e236STrond Myklebust {
6465c31e236STrond Myklebust 	struct nfs_delegation *delegation;
6475c31e236STrond Myklebust 	bool ret = false;
6485c31e236STrond Myklebust 
6495c31e236STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
6505c31e236STrond Myklebust 		nfs_mark_return_delegation(server, delegation);
6515c31e236STrond Myklebust 		ret = true;
6525c31e236STrond Myklebust 	}
6535c31e236STrond Myklebust 	return ret;
6545c31e236STrond Myklebust }
6555c31e236STrond Myklebust 
656b02ba0b6STrond Myklebust static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
657b02ba0b6STrond Myklebust {
658b02ba0b6STrond Myklebust 	struct nfs_server *server;
659b02ba0b6STrond Myklebust 
660b02ba0b6STrond Myklebust 	rcu_read_lock();
661b02ba0b6STrond Myklebust 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
662b02ba0b6STrond Myklebust 		nfs_server_mark_return_all_delegations(server);
663b02ba0b6STrond Myklebust 	rcu_read_unlock();
664b02ba0b6STrond Myklebust }
665b02ba0b6STrond Myklebust 
666b02ba0b6STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp)
667b02ba0b6STrond Myklebust {
668b02ba0b6STrond Myklebust 	if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
669b02ba0b6STrond Myklebust 		nfs4_schedule_state_manager(clp);
670b02ba0b6STrond Myklebust }
671b02ba0b6STrond Myklebust 
672b02ba0b6STrond Myklebust /**
673b02ba0b6STrond Myklebust  * nfs_expire_all_delegations
674b02ba0b6STrond Myklebust  * @clp: client to process
675b02ba0b6STrond Myklebust  *
676b02ba0b6STrond Myklebust  */
677b02ba0b6STrond Myklebust void nfs_expire_all_delegations(struct nfs_client *clp)
678b02ba0b6STrond Myklebust {
679b02ba0b6STrond Myklebust 	nfs_client_mark_return_all_delegations(clp);
680b02ba0b6STrond Myklebust 	nfs_delegation_run_state_manager(clp);
681b02ba0b6STrond Myklebust }
682b02ba0b6STrond Myklebust 
683d3978bb3SChuck Lever /**
684d3978bb3SChuck Lever  * nfs_super_return_all_delegations - return delegations for one superblock
685d3978bb3SChuck Lever  * @sb: sb to process
686d3978bb3SChuck Lever  *
6871da177e4SLinus Torvalds  */
688eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server)
6891da177e4SLinus Torvalds {
690d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
6915c31e236STrond Myklebust 	bool need_wait;
6921da177e4SLinus Torvalds 
6931da177e4SLinus Torvalds 	if (clp == NULL)
6941da177e4SLinus Torvalds 		return;
695d3978bb3SChuck Lever 
6968383e460STrond Myklebust 	rcu_read_lock();
6975c31e236STrond Myklebust 	need_wait = nfs_server_mark_return_all_delegations(server);
6988383e460STrond Myklebust 	rcu_read_unlock();
699d3978bb3SChuck Lever 
7005c31e236STrond Myklebust 	if (need_wait) {
701d18cc1fdSTrond Myklebust 		nfs4_schedule_state_manager(clp);
7025c31e236STrond Myklebust 		nfs4_wait_clnt_recover(clp);
7035c31e236STrond Myklebust 	}
704515d8611STrond Myklebust }
705515d8611STrond Myklebust 
706826e0013STrond Myklebust static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,
707d3978bb3SChuck Lever 						 fmode_t flags)
708515d8611STrond Myklebust {
709515d8611STrond Myklebust 	struct nfs_delegation *delegation;
710515d8611STrond Myklebust 
711d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
712c79571a5SAlexandros Batsakis 		if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
713c79571a5SAlexandros Batsakis 			continue;
714c79571a5SAlexandros Batsakis 		if (delegation->type & flags)
715826e0013STrond Myklebust 			nfs_mark_return_if_closed_delegation(server, delegation);
716707fb4b3STrond Myklebust 	}
717d3978bb3SChuck Lever }
718d3978bb3SChuck Lever 
719826e0013STrond Myklebust static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,
720d3978bb3SChuck Lever 							fmode_t flags)
721d3978bb3SChuck Lever {
722d3978bb3SChuck Lever 	struct nfs_server *server;
723d3978bb3SChuck Lever 
724d3978bb3SChuck Lever 	rcu_read_lock();
725d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
726826e0013STrond Myklebust 		nfs_mark_return_unused_delegation_types(server, flags);
727515d8611STrond Myklebust 	rcu_read_unlock();
7281da177e4SLinus Torvalds }
7291da177e4SLinus Torvalds 
73041020b67STrond Myklebust static void nfs_mark_delegation_revoked(struct nfs_server *server,
73141020b67STrond Myklebust 		struct nfs_delegation *delegation)
73241020b67STrond Myklebust {
73341020b67STrond Myklebust 	set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
734059b43e9STrond Myklebust 	delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
73541020b67STrond Myklebust 	nfs_mark_return_delegation(server, delegation);
73641020b67STrond Myklebust }
73741020b67STrond Myklebust 
73841020b67STrond Myklebust static bool nfs_revoke_delegation(struct inode *inode,
73941020b67STrond Myklebust 		const nfs4_stateid *stateid)
740869f9dfaSTrond Myklebust {
741869f9dfaSTrond Myklebust 	struct nfs_delegation *delegation;
7427f048831STrond Myklebust 	nfs4_stateid tmp;
74341020b67STrond Myklebust 	bool ret = false;
74441020b67STrond Myklebust 
745869f9dfaSTrond Myklebust 	rcu_read_lock();
746869f9dfaSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
74741020b67STrond Myklebust 	if (delegation == NULL)
74841020b67STrond Myklebust 		goto out;
7497f048831STrond Myklebust 	if (stateid == NULL) {
7507f048831STrond Myklebust 		nfs4_stateid_copy(&tmp, &delegation->stateid);
7517f048831STrond Myklebust 		stateid = &tmp;
7527f048831STrond Myklebust 	} else if (!nfs4_stateid_match(stateid, &delegation->stateid))
75341020b67STrond Myklebust 		goto out;
75441020b67STrond Myklebust 	nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
75541020b67STrond Myklebust 	ret = true;
75641020b67STrond Myklebust out:
757869f9dfaSTrond Myklebust 	rcu_read_unlock();
7587f048831STrond Myklebust 	if (ret)
7597f048831STrond Myklebust 		nfs_inode_find_state_and_recover(inode, stateid);
76041020b67STrond Myklebust 	return ret;
761869f9dfaSTrond Myklebust }
762869f9dfaSTrond Myklebust 
76341020b67STrond Myklebust void nfs_remove_bad_delegation(struct inode *inode,
76441020b67STrond Myklebust 		const nfs4_stateid *stateid)
765a1d0b5eeSTrond Myklebust {
766a1d0b5eeSTrond Myklebust 	struct nfs_delegation *delegation;
767a1d0b5eeSTrond Myklebust 
76841020b67STrond Myklebust 	if (!nfs_revoke_delegation(inode, stateid))
76941020b67STrond Myklebust 		return;
770d25be546STrond Myklebust 	delegation = nfs_inode_detach_delegation(inode);
7717f048831STrond Myklebust 	if (delegation)
772a1d0b5eeSTrond Myklebust 		nfs_free_delegation(delegation);
773a1d0b5eeSTrond Myklebust }
7749cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
775a1d0b5eeSTrond Myklebust 
776d3978bb3SChuck Lever /**
777826e0013STrond Myklebust  * nfs_expire_unused_delegation_types
778d3978bb3SChuck Lever  * @clp: client to process
779d3978bb3SChuck Lever  * @flags: delegation types to expire
780d3978bb3SChuck Lever  *
781d3978bb3SChuck Lever  */
782826e0013STrond Myklebust void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)
783c79571a5SAlexandros Batsakis {
784826e0013STrond Myklebust 	nfs_client_mark_return_unused_delegation_types(clp, flags);
785c79571a5SAlexandros Batsakis 	nfs_delegation_run_state_manager(clp);
786c79571a5SAlexandros Batsakis }
787c79571a5SAlexandros Batsakis 
788d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
789b7391f44STrond Myklebust {
790b7391f44STrond Myklebust 	struct nfs_delegation *delegation;
791b7391f44STrond Myklebust 
792d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
793b7391f44STrond Myklebust 		if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
794b7391f44STrond Myklebust 			continue;
795b757144fSTrond Myklebust 		nfs_mark_return_if_closed_delegation(server, delegation);
796b7391f44STrond Myklebust 	}
797b7391f44STrond Myklebust }
798b7391f44STrond Myklebust 
799d3978bb3SChuck Lever /**
800d3978bb3SChuck Lever  * nfs_expire_unreferenced_delegations - Eliminate unused delegations
801d3978bb3SChuck Lever  * @clp: nfs_client to process
802d3978bb3SChuck Lever  *
803d3978bb3SChuck Lever  */
804b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
805b7391f44STrond Myklebust {
806d3978bb3SChuck Lever 	struct nfs_server *server;
807d3978bb3SChuck Lever 
808d3978bb3SChuck Lever 	rcu_read_lock();
809d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
810d3978bb3SChuck Lever 		nfs_mark_return_unreferenced_delegations(server);
811d3978bb3SChuck Lever 	rcu_read_unlock();
812d3978bb3SChuck Lever 
813b7391f44STrond Myklebust 	nfs_delegation_run_state_manager(clp);
814b7391f44STrond Myklebust }
815b7391f44STrond Myklebust 
816d3978bb3SChuck Lever /**
817d3978bb3SChuck Lever  * nfs_async_inode_return_delegation - asynchronously return a delegation
818d3978bb3SChuck Lever  * @inode: inode to process
8198e663f0eSTrond Myklebust  * @stateid: state ID information
820d3978bb3SChuck Lever  *
821d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
8221da177e4SLinus Torvalds  */
823d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode,
824d3978bb3SChuck Lever 				      const nfs4_stateid *stateid)
8251da177e4SLinus Torvalds {
826ed1e6211STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
827ed1e6211STrond Myklebust 	struct nfs_client *clp = server->nfs_client;
8286411bd4aSTrond Myklebust 	struct nfs_delegation *delegation;
8291da177e4SLinus Torvalds 
8306411bd4aSTrond Myklebust 	rcu_read_lock();
8316411bd4aSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
832755a48a7STrond Myklebust 	if (delegation == NULL)
833755a48a7STrond Myklebust 		goto out_enoent;
8344816fdadSTrond Myklebust 	if (stateid != NULL &&
8354816fdadSTrond Myklebust 	    !clp->cl_mvops->match_stateid(&delegation->stateid, stateid))
836755a48a7STrond Myklebust 		goto out_enoent;
837ed1e6211STrond Myklebust 	nfs_mark_return_delegation(server, delegation);
8386411bd4aSTrond Myklebust 	rcu_read_unlock();
839d3978bb3SChuck Lever 
8406411bd4aSTrond Myklebust 	nfs_delegation_run_state_manager(clp);
8416411bd4aSTrond Myklebust 	return 0;
842755a48a7STrond Myklebust out_enoent:
843755a48a7STrond Myklebust 	rcu_read_unlock();
844755a48a7STrond Myklebust 	return -ENOENT;
8451da177e4SLinus Torvalds }
8461da177e4SLinus Torvalds 
847d3978bb3SChuck Lever static struct inode *
848d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server,
849d3978bb3SChuck Lever 				 const struct nfs_fh *fhandle)
8501da177e4SLinus Torvalds {
8511da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
8521da177e4SLinus Torvalds 	struct inode *res = NULL;
853d3978bb3SChuck Lever 
854d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
85586e89489STrond Myklebust 		spin_lock(&delegation->lock);
85686e89489STrond Myklebust 		if (delegation->inode != NULL &&
85786e89489STrond Myklebust 		    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
8581da177e4SLinus Torvalds 			res = igrab(delegation->inode);
85986e89489STrond Myklebust 			spin_unlock(&delegation->lock);
86086e89489STrond Myklebust 			if (res != NULL)
861d3978bb3SChuck Lever 				return res;
8626c342655STrond Myklebust 			return ERR_PTR(-EAGAIN);
8636c342655STrond Myklebust 		}
8646c342655STrond Myklebust 		spin_unlock(&delegation->lock);
8656c342655STrond Myklebust 	}
8666c342655STrond Myklebust 	return ERR_PTR(-ENOENT);
867d3978bb3SChuck Lever }
868d3978bb3SChuck Lever 
869d3978bb3SChuck Lever /**
870d3978bb3SChuck Lever  * nfs_delegation_find_inode - retrieve the inode associated with a delegation
871d3978bb3SChuck Lever  * @clp: client state handle
872d3978bb3SChuck Lever  * @fhandle: filehandle from a delegation recall
873d3978bb3SChuck Lever  *
874d3978bb3SChuck Lever  * Returns pointer to inode matching "fhandle," or NULL if a matching inode
875d3978bb3SChuck Lever  * cannot be found.
876d3978bb3SChuck Lever  */
877d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
878d3978bb3SChuck Lever 					const struct nfs_fh *fhandle)
879d3978bb3SChuck Lever {
880d3978bb3SChuck Lever 	struct nfs_server *server;
8816c342655STrond Myklebust 	struct inode *res;
882d3978bb3SChuck Lever 
883d3978bb3SChuck Lever 	rcu_read_lock();
884d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
885d3978bb3SChuck Lever 		res = nfs_delegation_find_inode_server(server, fhandle);
8866c342655STrond Myklebust 		if (res != ERR_PTR(-ENOENT))
8876c342655STrond Myklebust 			return res;
888d3978bb3SChuck Lever 	}
8898383e460STrond Myklebust 	rcu_read_unlock();
8906c342655STrond Myklebust 	return ERR_PTR(-ENOENT);
8911da177e4SLinus Torvalds }
8921da177e4SLinus Torvalds 
893d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
894d3978bb3SChuck Lever {
895d3978bb3SChuck Lever 	struct nfs_delegation *delegation;
896d3978bb3SChuck Lever 
89745870d69STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
89845870d69STrond Myklebust 		/*
89945870d69STrond Myklebust 		 * If the delegation may have been admin revoked, then we
90045870d69STrond Myklebust 		 * cannot reclaim it.
90145870d69STrond Myklebust 		 */
90245870d69STrond Myklebust 		if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags))
90345870d69STrond Myklebust 			continue;
904d3978bb3SChuck Lever 		set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
905d3978bb3SChuck Lever 	}
90645870d69STrond Myklebust }
907d3978bb3SChuck Lever 
908d3978bb3SChuck Lever /**
909d3978bb3SChuck Lever  * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed
910d3978bb3SChuck Lever  * @clp: nfs_client to process
911d3978bb3SChuck Lever  *
9121da177e4SLinus Torvalds  */
913adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp)
9141da177e4SLinus Torvalds {
915d3978bb3SChuck Lever 	struct nfs_server *server;
916d3978bb3SChuck Lever 
9178383e460STrond Myklebust 	rcu_read_lock();
918d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
919d3978bb3SChuck Lever 		nfs_delegation_mark_reclaim_server(server);
9208383e460STrond Myklebust 	rcu_read_unlock();
9211da177e4SLinus Torvalds }
9221da177e4SLinus Torvalds 
923d3978bb3SChuck Lever /**
924d3978bb3SChuck Lever  * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done
925d3978bb3SChuck Lever  * @clp: nfs_client to process
926d3978bb3SChuck Lever  *
9271da177e4SLinus Torvalds  */
928adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
9291da177e4SLinus Torvalds {
9308383e460STrond Myklebust 	struct nfs_delegation *delegation;
931d3978bb3SChuck Lever 	struct nfs_server *server;
93286e89489STrond Myklebust 	struct inode *inode;
933d3978bb3SChuck Lever 
9348383e460STrond Myklebust restart:
9358383e460STrond Myklebust 	rcu_read_lock();
936d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
937d3978bb3SChuck Lever 		list_for_each_entry_rcu(delegation, &server->delegations,
938d3978bb3SChuck Lever 								super_list) {
939ec3ca4e5STrond Myklebust 			if (test_bit(NFS_DELEGATION_RETURNING,
940ec3ca4e5STrond Myklebust 						&delegation->flags))
941ec3ca4e5STrond Myklebust 				continue;
942d3978bb3SChuck Lever 			if (test_bit(NFS_DELEGATION_NEED_RECLAIM,
943d3978bb3SChuck Lever 						&delegation->flags) == 0)
9441da177e4SLinus Torvalds 				continue;
9459f0f8e12STrond Myklebust 			if (!nfs_sb_active(server->super))
946f3893491SNeilBrown 				break; /* continue in outer loop */
9479f0f8e12STrond Myklebust 			inode = nfs_delegation_grab_inode(delegation);
9489f0f8e12STrond Myklebust 			if (inode == NULL) {
9499f0f8e12STrond Myklebust 				rcu_read_unlock();
9509f0f8e12STrond Myklebust 				nfs_sb_deactive(server->super);
9519f0f8e12STrond Myklebust 				goto restart;
9529f0f8e12STrond Myklebust 			}
953b04b22f4STrond Myklebust 			delegation = nfs_start_delegation_return_locked(NFS_I(inode));
954b04b22f4STrond Myklebust 			rcu_read_unlock();
955b04b22f4STrond Myklebust 			if (delegation != NULL) {
956d3978bb3SChuck Lever 				delegation = nfs_detach_delegation(NFS_I(inode),
957d25be546STrond Myklebust 					delegation, server);
9588383e460STrond Myklebust 				if (delegation != NULL)
959905f8d16STrond Myklebust 					nfs_free_delegation(delegation);
960b04b22f4STrond Myklebust 			}
96186e89489STrond Myklebust 			iput(inode);
9629f0f8e12STrond Myklebust 			nfs_sb_deactive(server->super);
9633ca951b6SNeilBrown 			cond_resched();
9648383e460STrond Myklebust 			goto restart;
9651da177e4SLinus Torvalds 		}
966d3978bb3SChuck Lever 	}
9678383e460STrond Myklebust 	rcu_read_unlock();
9681da177e4SLinus Torvalds }
9693e4f6290STrond Myklebust 
970bb3d1a3bSTrond Myklebust static inline bool nfs4_server_rebooted(const struct nfs_client *clp)
971bb3d1a3bSTrond Myklebust {
972bb3d1a3bSTrond Myklebust 	return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) |
973bb3d1a3bSTrond Myklebust 				BIT(NFS4CLNT_LEASE_EXPIRED) |
974bb3d1a3bSTrond Myklebust 				BIT(NFS4CLNT_SESSION_RESET))) != 0;
975bb3d1a3bSTrond Myklebust }
976bb3d1a3bSTrond Myklebust 
97745870d69STrond Myklebust static void nfs_mark_test_expired_delegation(struct nfs_server *server,
97845870d69STrond Myklebust 	    struct nfs_delegation *delegation)
97945870d69STrond Myklebust {
980059b43e9STrond Myklebust 	if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE)
981059b43e9STrond Myklebust 		return;
98245870d69STrond Myklebust 	clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
98345870d69STrond Myklebust 	set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
98445870d69STrond Myklebust 	set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state);
98545870d69STrond Myklebust }
98645870d69STrond Myklebust 
987bb3d1a3bSTrond Myklebust static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server,
988bb3d1a3bSTrond Myklebust 		struct inode *inode)
989bb3d1a3bSTrond Myklebust {
990bb3d1a3bSTrond Myklebust 	struct nfs_delegation *delegation;
991bb3d1a3bSTrond Myklebust 
992bb3d1a3bSTrond Myklebust 	rcu_read_lock();
993bb3d1a3bSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
994bb3d1a3bSTrond Myklebust 	if (delegation)
995bb3d1a3bSTrond Myklebust 		nfs_mark_test_expired_delegation(server, delegation);
996bb3d1a3bSTrond Myklebust 	rcu_read_unlock();
997bb3d1a3bSTrond Myklebust 
998bb3d1a3bSTrond Myklebust }
999bb3d1a3bSTrond Myklebust 
100045870d69STrond Myklebust static void nfs_delegation_mark_test_expired_server(struct nfs_server *server)
100145870d69STrond Myklebust {
100245870d69STrond Myklebust 	struct nfs_delegation *delegation;
100345870d69STrond Myklebust 
100445870d69STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list)
100545870d69STrond Myklebust 		nfs_mark_test_expired_delegation(server, delegation);
100645870d69STrond Myklebust }
100745870d69STrond Myklebust 
100845870d69STrond Myklebust /**
100945870d69STrond Myklebust  * nfs_mark_test_expired_all_delegations - mark all delegations for testing
101045870d69STrond Myklebust  * @clp: nfs_client to process
101145870d69STrond Myklebust  *
101245870d69STrond Myklebust  * Iterates through all the delegations associated with this server and
101345870d69STrond Myklebust  * marks them as needing to be checked for validity.
101445870d69STrond Myklebust  */
101545870d69STrond Myklebust void nfs_mark_test_expired_all_delegations(struct nfs_client *clp)
101645870d69STrond Myklebust {
101745870d69STrond Myklebust 	struct nfs_server *server;
101845870d69STrond Myklebust 
101945870d69STrond Myklebust 	rcu_read_lock();
102045870d69STrond Myklebust 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
102145870d69STrond Myklebust 		nfs_delegation_mark_test_expired_server(server);
102245870d69STrond Myklebust 	rcu_read_unlock();
102345870d69STrond Myklebust }
102445870d69STrond Myklebust 
102545870d69STrond Myklebust /**
102645870d69STrond Myklebust  * nfs_reap_expired_delegations - reap expired delegations
102745870d69STrond Myklebust  * @clp: nfs_client to process
102845870d69STrond Myklebust  *
102945870d69STrond Myklebust  * Iterates through all the delegations associated with this server and
103045870d69STrond Myklebust  * checks if they have may have been revoked. This function is usually
103145870d69STrond Myklebust  * expected to be called in cases where the server may have lost its
103245870d69STrond Myklebust  * lease.
103345870d69STrond Myklebust  */
103445870d69STrond Myklebust void nfs_reap_expired_delegations(struct nfs_client *clp)
103545870d69STrond Myklebust {
103645870d69STrond Myklebust 	const struct nfs4_minor_version_ops *ops = clp->cl_mvops;
103745870d69STrond Myklebust 	struct nfs_delegation *delegation;
103845870d69STrond Myklebust 	struct nfs_server *server;
103945870d69STrond Myklebust 	struct inode *inode;
104045870d69STrond Myklebust 	struct rpc_cred *cred;
104145870d69STrond Myklebust 	nfs4_stateid stateid;
104245870d69STrond Myklebust 
104345870d69STrond Myklebust restart:
104445870d69STrond Myklebust 	rcu_read_lock();
104545870d69STrond Myklebust 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
104645870d69STrond Myklebust 		list_for_each_entry_rcu(delegation, &server->delegations,
104745870d69STrond Myklebust 								super_list) {
104845870d69STrond Myklebust 			if (test_bit(NFS_DELEGATION_RETURNING,
104945870d69STrond Myklebust 						&delegation->flags))
105045870d69STrond Myklebust 				continue;
105145870d69STrond Myklebust 			if (test_bit(NFS_DELEGATION_TEST_EXPIRED,
105245870d69STrond Myklebust 						&delegation->flags) == 0)
105345870d69STrond Myklebust 				continue;
105445870d69STrond Myklebust 			if (!nfs_sb_active(server->super))
1055f3893491SNeilBrown 				break; /* continue in outer loop */
105645870d69STrond Myklebust 			inode = nfs_delegation_grab_inode(delegation);
105745870d69STrond Myklebust 			if (inode == NULL) {
105845870d69STrond Myklebust 				rcu_read_unlock();
105945870d69STrond Myklebust 				nfs_sb_deactive(server->super);
106045870d69STrond Myklebust 				goto restart;
106145870d69STrond Myklebust 			}
106245870d69STrond Myklebust 			cred = get_rpccred_rcu(delegation->cred);
106345870d69STrond Myklebust 			nfs4_stateid_copy(&stateid, &delegation->stateid);
106445870d69STrond Myklebust 			clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
106545870d69STrond Myklebust 			rcu_read_unlock();
106645870d69STrond Myklebust 			if (cred != NULL &&
106745870d69STrond Myklebust 			    ops->test_and_free_expired(server, &stateid, cred) < 0) {
106845870d69STrond Myklebust 				nfs_revoke_delegation(inode, &stateid);
106945870d69STrond Myklebust 				nfs_inode_find_state_and_recover(inode, &stateid);
107045870d69STrond Myklebust 			}
107145870d69STrond Myklebust 			put_rpccred(cred);
1072bb3d1a3bSTrond Myklebust 			if (nfs4_server_rebooted(clp)) {
1073bb3d1a3bSTrond Myklebust 				nfs_inode_mark_test_expired_delegation(server,inode);
1074bb3d1a3bSTrond Myklebust 				iput(inode);
1075bb3d1a3bSTrond Myklebust 				nfs_sb_deactive(server->super);
1076bb3d1a3bSTrond Myklebust 				return;
1077bb3d1a3bSTrond Myklebust 			}
107845870d69STrond Myklebust 			iput(inode);
107945870d69STrond Myklebust 			nfs_sb_deactive(server->super);
10803ca951b6SNeilBrown 			cond_resched();
108145870d69STrond Myklebust 			goto restart;
108245870d69STrond Myklebust 		}
108345870d69STrond Myklebust 	}
108445870d69STrond Myklebust 	rcu_read_unlock();
108545870d69STrond Myklebust }
108645870d69STrond Myklebust 
10876c2d8f8dSTrond Myklebust void nfs_inode_find_delegation_state_and_recover(struct inode *inode,
10886c2d8f8dSTrond Myklebust 		const nfs4_stateid *stateid)
10896c2d8f8dSTrond Myklebust {
10906c2d8f8dSTrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
10916c2d8f8dSTrond Myklebust 	struct nfs_delegation *delegation;
10926c2d8f8dSTrond Myklebust 	bool found = false;
10936c2d8f8dSTrond Myklebust 
10946c2d8f8dSTrond Myklebust 	rcu_read_lock();
10956c2d8f8dSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
10966c2d8f8dSTrond Myklebust 	if (delegation &&
10976c2d8f8dSTrond Myklebust 	    nfs4_stateid_match_other(&delegation->stateid, stateid)) {
10986c2d8f8dSTrond Myklebust 		nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation);
10996c2d8f8dSTrond Myklebust 		found = true;
11006c2d8f8dSTrond Myklebust 	}
11016c2d8f8dSTrond Myklebust 	rcu_read_unlock();
11026c2d8f8dSTrond Myklebust 	if (found)
11036c2d8f8dSTrond Myklebust 		nfs4_schedule_state_manager(clp);
11046c2d8f8dSTrond Myklebust }
11056c2d8f8dSTrond Myklebust 
1106d3978bb3SChuck Lever /**
1107d3978bb3SChuck Lever  * nfs_delegations_present - check for existence of delegations
1108d3978bb3SChuck Lever  * @clp: client state handle
1109d3978bb3SChuck Lever  *
1110d3978bb3SChuck Lever  * Returns one if there are any nfs_delegation structures attached
1111d3978bb3SChuck Lever  * to this nfs_client.
1112d3978bb3SChuck Lever  */
1113d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp)
1114d3978bb3SChuck Lever {
1115d3978bb3SChuck Lever 	struct nfs_server *server;
1116d3978bb3SChuck Lever 	int ret = 0;
1117d3978bb3SChuck Lever 
1118d3978bb3SChuck Lever 	rcu_read_lock();
1119d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
1120d3978bb3SChuck Lever 		if (!list_empty(&server->delegations)) {
1121d3978bb3SChuck Lever 			ret = 1;
1122d3978bb3SChuck Lever 			break;
1123d3978bb3SChuck Lever 		}
1124d3978bb3SChuck Lever 	rcu_read_unlock();
1125d3978bb3SChuck Lever 	return ret;
1126d3978bb3SChuck Lever }
1127d3978bb3SChuck Lever 
1128d3978bb3SChuck Lever /**
112912f275cdSTrond Myklebust  * nfs4_refresh_delegation_stateid - Update delegation stateid seqid
113012f275cdSTrond Myklebust  * @dst: stateid to refresh
113112f275cdSTrond Myklebust  * @inode: inode to check
113212f275cdSTrond Myklebust  *
113312f275cdSTrond Myklebust  * Returns "true" and updates "dst->seqid" * if inode had a delegation
113412f275cdSTrond Myklebust  * that matches our delegation stateid. Otherwise "false" is returned.
113512f275cdSTrond Myklebust  */
113612f275cdSTrond Myklebust bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
113712f275cdSTrond Myklebust {
113812f275cdSTrond Myklebust 	struct nfs_delegation *delegation;
113912f275cdSTrond Myklebust 	bool ret = false;
114012f275cdSTrond Myklebust 	if (!inode)
114112f275cdSTrond Myklebust 		goto out;
114212f275cdSTrond Myklebust 
114312f275cdSTrond Myklebust 	rcu_read_lock();
114412f275cdSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
114512f275cdSTrond Myklebust 	if (delegation != NULL &&
114612f275cdSTrond Myklebust 	    nfs4_stateid_match_other(dst, &delegation->stateid)) {
114712f275cdSTrond Myklebust 		dst->seqid = delegation->stateid.seqid;
114812f275cdSTrond Myklebust 		return ret;
114912f275cdSTrond Myklebust 	}
115012f275cdSTrond Myklebust 	rcu_read_unlock();
115112f275cdSTrond Myklebust out:
115212f275cdSTrond Myklebust 	return ret;
115312f275cdSTrond Myklebust }
115412f275cdSTrond Myklebust 
115512f275cdSTrond Myklebust /**
1156d3978bb3SChuck Lever  * nfs4_copy_delegation_stateid - Copy inode's state ID information
1157d3978bb3SChuck Lever  * @inode: inode to check
11580032a7a7STrond Myklebust  * @flags: delegation type requirement
1159abf4e13cSTrond Myklebust  * @dst: stateid data structure to fill in
1160abf4e13cSTrond Myklebust  * @cred: optional argument to retrieve credential
1161d3978bb3SChuck Lever  *
11620032a7a7STrond Myklebust  * Returns "true" and fills in "dst->data" * if inode had a delegation,
11630032a7a7STrond Myklebust  * otherwise "false" is returned.
1164d3978bb3SChuck Lever  */
1165abf4e13cSTrond Myklebust bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags,
1166abf4e13cSTrond Myklebust 		nfs4_stateid *dst, struct rpc_cred **cred)
11673e4f6290STrond Myklebust {
11683e4f6290STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
11693e4f6290STrond Myklebust 	struct nfs_delegation *delegation;
11700032a7a7STrond Myklebust 	bool ret;
11713e4f6290STrond Myklebust 
11720032a7a7STrond Myklebust 	flags &= FMODE_READ|FMODE_WRITE;
11738383e460STrond Myklebust 	rcu_read_lock();
11748383e460STrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
1175aa05c87fSTrond Myklebust 	ret = nfs4_is_valid_delegation(delegation, flags);
11760032a7a7STrond Myklebust 	if (ret) {
1177f597c537STrond Myklebust 		nfs4_stateid_copy(dst, &delegation->stateid);
11780032a7a7STrond Myklebust 		nfs_mark_delegation_referenced(delegation);
1179abf4e13cSTrond Myklebust 		if (cred)
1180abf4e13cSTrond Myklebust 			*cred = get_rpccred(delegation->cred);
11813e4f6290STrond Myklebust 	}
11828383e460STrond Myklebust 	rcu_read_unlock();
11838383e460STrond Myklebust 	return ret;
11843e4f6290STrond Myklebust }
11855445b1fbSTrond Myklebust 
11865445b1fbSTrond Myklebust /**
11875445b1fbSTrond Myklebust  * nfs4_delegation_flush_on_close - Check if we must flush file on close
11885445b1fbSTrond Myklebust  * @inode: inode to check
11895445b1fbSTrond Myklebust  *
11905445b1fbSTrond Myklebust  * This function checks the number of outstanding writes to the file
11915445b1fbSTrond Myklebust  * against the delegation 'space_limit' field to see if
11925445b1fbSTrond Myklebust  * the spec requires us to flush the file on close.
11935445b1fbSTrond Myklebust  */
11945445b1fbSTrond Myklebust bool nfs4_delegation_flush_on_close(const struct inode *inode)
11955445b1fbSTrond Myklebust {
11965445b1fbSTrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
11975445b1fbSTrond Myklebust 	struct nfs_delegation *delegation;
11985445b1fbSTrond Myklebust 	bool ret = true;
11995445b1fbSTrond Myklebust 
12005445b1fbSTrond Myklebust 	rcu_read_lock();
12015445b1fbSTrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
12025445b1fbSTrond Myklebust 	if (delegation == NULL || !(delegation->type & FMODE_WRITE))
12035445b1fbSTrond Myklebust 		goto out;
1204a6b6d5b8STrond Myklebust 	if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit)
12055445b1fbSTrond Myklebust 		ret = false;
12065445b1fbSTrond Myklebust out:
12075445b1fbSTrond Myklebust 	rcu_read_unlock();
12085445b1fbSTrond Myklebust 	return ret;
12095445b1fbSTrond Myklebust }
1210