xref: /openbmc/linux/fs/nfs/delegation.c (revision b7b7dac6)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/fs/nfs/delegation.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 2004 Trond Myklebust
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * NFS file delegation management
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds #include <linux/completion.h>
1158d9714aSTrond Myklebust #include <linux/kthread.h>
121da177e4SLinus Torvalds #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/sched.h>
145a0e3ad6STejun Heo #include <linux/slab.h>
151da177e4SLinus Torvalds #include <linux/spinlock.h>
161eb5d98fSJeff Layton #include <linux/iversion.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #include <linux/nfs4.h>
191da177e4SLinus Torvalds #include <linux/nfs_fs.h>
201da177e4SLinus Torvalds #include <linux/nfs_xdr.h>
211da177e4SLinus Torvalds 
224ce79717STrond Myklebust #include "nfs4_fs.h"
23c01d3645STrond Myklebust #include "nfs4session.h"
241da177e4SLinus Torvalds #include "delegation.h"
2524c8dbbbSDavid Howells #include "internal.h"
26ca8acf8dSTrond Myklebust #include "nfs4trace.h"
271da177e4SLinus Torvalds 
28905f8d16STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation)
29905f8d16STrond Myklebust {
30a52458b4SNeilBrown 	put_cred(delegation->cred);
31e00b8a24STrond Myklebust 	delegation->cred = NULL;
3226f04ddeSLai Jiangshan 	kfree_rcu(delegation, rcu);
338383e460STrond Myklebust }
348383e460STrond Myklebust 
35d3978bb3SChuck Lever /**
36d3978bb3SChuck Lever  * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
37d3978bb3SChuck Lever  * @delegation: delegation to process
38d3978bb3SChuck Lever  *
39d3978bb3SChuck Lever  */
40b7391f44STrond Myklebust void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
41b7391f44STrond Myklebust {
42b7391f44STrond Myklebust 	set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
43b7391f44STrond Myklebust }
44b7391f44STrond Myklebust 
45aa05c87fSTrond Myklebust static bool
46aa05c87fSTrond Myklebust nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
47aa05c87fSTrond Myklebust 		fmode_t flags)
48aa05c87fSTrond Myklebust {
49aa05c87fSTrond Myklebust 	if (delegation != NULL && (delegation->type & flags) == flags &&
50aa05c87fSTrond Myklebust 	    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
51aa05c87fSTrond Myklebust 	    !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
52aa05c87fSTrond Myklebust 		return true;
53aa05c87fSTrond Myklebust 	return false;
54aa05c87fSTrond Myklebust }
55aa05c87fSTrond Myklebust 
56be3df3ddSTrond Myklebust struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode)
57be3df3ddSTrond Myklebust {
58be3df3ddSTrond Myklebust 	struct nfs_delegation *delegation;
59be3df3ddSTrond Myklebust 
60be3df3ddSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
61be3df3ddSTrond Myklebust 	if (nfs4_is_valid_delegation(delegation, 0))
62be3df3ddSTrond Myklebust 		return delegation;
63be3df3ddSTrond Myklebust 	return NULL;
64be3df3ddSTrond Myklebust }
65be3df3ddSTrond Myklebust 
6615bb3afeSPeng Tao static int
6715bb3afeSPeng Tao nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
68b7391f44STrond Myklebust {
69b7391f44STrond Myklebust 	struct nfs_delegation *delegation;
70b7391f44STrond Myklebust 	int ret = 0;
71b7391f44STrond Myklebust 
72b7391f44STrond Myklebust 	flags &= FMODE_READ|FMODE_WRITE;
73b7391f44STrond Myklebust 	rcu_read_lock();
74b7391f44STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
75aa05c87fSTrond Myklebust 	if (nfs4_is_valid_delegation(delegation, flags)) {
7615bb3afeSPeng Tao 		if (mark)
77b7391f44STrond Myklebust 			nfs_mark_delegation_referenced(delegation);
78b7391f44STrond Myklebust 		ret = 1;
79b7391f44STrond Myklebust 	}
80b7391f44STrond Myklebust 	rcu_read_unlock();
81b7391f44STrond Myklebust 	return ret;
82b7391f44STrond Myklebust }
8315bb3afeSPeng Tao /**
8415bb3afeSPeng Tao  * nfs_have_delegation - check if inode has a delegation, mark it
8515bb3afeSPeng Tao  * NFS_DELEGATION_REFERENCED if there is one.
8615bb3afeSPeng Tao  * @inode: inode to check
8715bb3afeSPeng Tao  * @flags: delegation types to check for
8815bb3afeSPeng Tao  *
8915bb3afeSPeng Tao  * Returns one if inode has the indicated delegation, otherwise zero.
9015bb3afeSPeng Tao  */
9115bb3afeSPeng Tao int nfs4_have_delegation(struct inode *inode, fmode_t flags)
9215bb3afeSPeng Tao {
9315bb3afeSPeng Tao 	return nfs4_do_check_delegation(inode, flags, true);
9415bb3afeSPeng Tao }
9515bb3afeSPeng Tao 
9615bb3afeSPeng Tao /*
9715bb3afeSPeng Tao  * nfs4_check_delegation - check if inode has a delegation, do not mark
9815bb3afeSPeng Tao  * NFS_DELEGATION_REFERENCED if it has one.
9915bb3afeSPeng Tao  */
10015bb3afeSPeng Tao int nfs4_check_delegation(struct inode *inode, fmode_t flags)
10115bb3afeSPeng Tao {
10215bb3afeSPeng Tao 	return nfs4_do_check_delegation(inode, flags, false);
10315bb3afeSPeng Tao }
104b7391f44STrond Myklebust 
10544f411c3SOlga Kornievskaia static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid)
106888e694cSTrond Myklebust {
107888e694cSTrond Myklebust 	struct inode *inode = state->inode;
108888e694cSTrond Myklebust 	struct file_lock *fl;
109bd61e0a9SJeff Layton 	struct file_lock_context *flctx = inode->i_flctx;
110bd61e0a9SJeff Layton 	struct list_head *list;
111d5122201STrond Myklebust 	int status = 0;
112888e694cSTrond Myklebust 
113bd61e0a9SJeff Layton 	if (flctx == NULL)
11465b62a29STrond Myklebust 		goto out;
115314d7cc0SJeff Layton 
116bd61e0a9SJeff Layton 	list = &flctx->flc_posix;
1176109c850SJeff Layton 	spin_lock(&flctx->flc_lock);
118bd61e0a9SJeff Layton restart:
119bd61e0a9SJeff Layton 	list_for_each_entry(fl, list, fl_list) {
12044f411c3SOlga Kornievskaia 		if (nfs_file_open_context(fl->fl_file)->state != state)
121888e694cSTrond Myklebust 			continue;
1226109c850SJeff Layton 		spin_unlock(&flctx->flc_lock);
123db4f2e63STrond Myklebust 		status = nfs4_lock_delegation_recall(fl, state, stateid);
124d5122201STrond Myklebust 		if (status < 0)
1253f09df70STrond Myklebust 			goto out;
1266109c850SJeff Layton 		spin_lock(&flctx->flc_lock);
127888e694cSTrond Myklebust 	}
128bd61e0a9SJeff Layton 	if (list == &flctx->flc_posix) {
129bd61e0a9SJeff Layton 		list = &flctx->flc_flock;
130bd61e0a9SJeff Layton 		goto restart;
1315263e31eSJeff Layton 	}
1326109c850SJeff Layton 	spin_unlock(&flctx->flc_lock);
1333f09df70STrond Myklebust out:
134888e694cSTrond Myklebust 	return status;
135888e694cSTrond Myklebust }
136888e694cSTrond Myklebust 
13724311f88STrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode,
13824311f88STrond Myklebust 		const nfs4_stateid *stateid, fmode_t type)
1391da177e4SLinus Torvalds {
1401da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
1411da177e4SLinus Torvalds 	struct nfs_open_context *ctx;
142d25be546STrond Myklebust 	struct nfs4_state_owner *sp;
1431da177e4SLinus Torvalds 	struct nfs4_state *state;
144d25be546STrond Myklebust 	unsigned int seq;
145888e694cSTrond Myklebust 	int err;
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds again:
1480de43976STrond Myklebust 	rcu_read_lock();
1490de43976STrond Myklebust 	list_for_each_entry_rcu(ctx, &nfsi->open_files, list) {
1501da177e4SLinus Torvalds 		state = ctx->state;
1511da177e4SLinus Torvalds 		if (state == NULL)
1521da177e4SLinus Torvalds 			continue;
1531da177e4SLinus Torvalds 		if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
1541da177e4SLinus Torvalds 			continue;
155f8ebf7a8STrond Myklebust 		if (!nfs4_valid_open_stateid(state))
156f8ebf7a8STrond Myklebust 			continue;
157f597c537STrond Myklebust 		if (!nfs4_stateid_match(&state->stateid, stateid))
15890163027STrond Myklebust 			continue;
1590de43976STrond Myklebust 		if (!get_nfs_open_context(ctx))
1600de43976STrond Myklebust 			continue;
1610de43976STrond Myklebust 		rcu_read_unlock();
162d25be546STrond Myklebust 		sp = state->owner;
16365b62a29STrond Myklebust 		/* Block nfs4_proc_unlck */
16465b62a29STrond Myklebust 		mutex_lock(&sp->so_delegreturn_mutex);
165d25be546STrond Myklebust 		seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
1665eb8d18cSTrond Myklebust 		err = nfs4_open_delegation_recall(ctx, state, stateid);
167d25be546STrond Myklebust 		if (!err)
16844f411c3SOlga Kornievskaia 			err = nfs_delegation_claim_locks(state, stateid);
169d25be546STrond Myklebust 		if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
170d25be546STrond Myklebust 			err = -EAGAIN;
17165b62a29STrond Myklebust 		mutex_unlock(&sp->so_delegreturn_mutex);
1721da177e4SLinus Torvalds 		put_nfs_open_context(ctx);
173888e694cSTrond Myklebust 		if (err != 0)
174d18cc1fdSTrond Myklebust 			return err;
1751da177e4SLinus Torvalds 		goto again;
1761da177e4SLinus Torvalds 	}
1770de43976STrond Myklebust 	rcu_read_unlock();
178d18cc1fdSTrond Myklebust 	return 0;
1791da177e4SLinus Torvalds }
1801da177e4SLinus Torvalds 
181d3978bb3SChuck Lever /**
182d3978bb3SChuck Lever  * nfs_inode_reclaim_delegation - process a delegation reclaim request
183d3978bb3SChuck Lever  * @inode: inode to process
184d3978bb3SChuck Lever  * @cred: credential to use for request
18535156bffSTrond Myklebust  * @type: delegation type
18635156bffSTrond Myklebust  * @stateid: delegation stateid
18735156bffSTrond Myklebust  * @pagemod_limit: write delegation "space_limit"
188d3978bb3SChuck Lever  *
1891da177e4SLinus Torvalds  */
190a52458b4SNeilBrown void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
19135156bffSTrond Myklebust 				  fmode_t type,
19235156bffSTrond Myklebust 				  const nfs4_stateid *stateid,
19335156bffSTrond Myklebust 				  unsigned long pagemod_limit)
1941da177e4SLinus Torvalds {
1958f649c37STrond Myklebust 	struct nfs_delegation *delegation;
196a52458b4SNeilBrown 	const struct cred *oldcred = NULL;
1971da177e4SLinus Torvalds 
1988f649c37STrond Myklebust 	rcu_read_lock();
1998f649c37STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
2008f649c37STrond Myklebust 	if (delegation != NULL) {
2018f649c37STrond Myklebust 		spin_lock(&delegation->lock);
2021deed572STrond Myklebust 		if (nfs4_is_valid_delegation(delegation, 0)) {
20335156bffSTrond Myklebust 			nfs4_stateid_copy(&delegation->stateid, stateid);
20435156bffSTrond Myklebust 			delegation->type = type;
20535156bffSTrond Myklebust 			delegation->pagemod_limit = pagemod_limit;
20605c88babSTrond Myklebust 			oldcred = delegation->cred;
207a52458b4SNeilBrown 			delegation->cred = get_cred(cred);
2088f649c37STrond Myklebust 			clear_bit(NFS_DELEGATION_NEED_RECLAIM,
2098f649c37STrond Myklebust 				  &delegation->flags);
2108f649c37STrond Myklebust 			spin_unlock(&delegation->lock);
2118f649c37STrond Myklebust 			rcu_read_unlock();
212a52458b4SNeilBrown 			put_cred(oldcred);
21335156bffSTrond Myklebust 			trace_nfs4_reclaim_delegation(inode, type);
214b1a318deSTrond Myklebust 			return;
215b1a318deSTrond Myklebust 		}
2168f649c37STrond Myklebust 		/* We appear to have raced with a delegation return. */
2178f649c37STrond Myklebust 		spin_unlock(&delegation->lock);
218b1a318deSTrond Myklebust 	}
2198f649c37STrond Myklebust 	rcu_read_unlock();
22035156bffSTrond Myklebust 	nfs_inode_set_delegation(inode, cred, type, stateid, pagemod_limit);
2218f649c37STrond Myklebust }
2221da177e4SLinus Torvalds 
22357bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
22457bfa891STrond Myklebust {
22557bfa891STrond Myklebust 	int res = 0;
22657bfa891STrond Myklebust 
227869f9dfaSTrond Myklebust 	if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
228869f9dfaSTrond Myklebust 		res = nfs4_proc_delegreturn(inode,
229869f9dfaSTrond Myklebust 				delegation->cred,
230869f9dfaSTrond Myklebust 				&delegation->stateid,
231869f9dfaSTrond Myklebust 				issync);
23257bfa891STrond Myklebust 	return res;
23357bfa891STrond Myklebust }
23457bfa891STrond Myklebust 
23586e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
23686e89489STrond Myklebust {
23786e89489STrond Myklebust 	struct inode *inode = NULL;
23886e89489STrond Myklebust 
23986e89489STrond Myklebust 	spin_lock(&delegation->lock);
24086e89489STrond Myklebust 	if (delegation->inode != NULL)
24186e89489STrond Myklebust 		inode = igrab(delegation->inode);
2426f9449beSTrond Myklebust 	if (!inode)
2436f9449beSTrond Myklebust 		set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
24486e89489STrond Myklebust 	spin_unlock(&delegation->lock);
24586e89489STrond Myklebust 	return inode;
24686e89489STrond Myklebust }
24786e89489STrond Myklebust 
248dda4b225SChuck Lever static struct nfs_delegation *
249d25be546STrond Myklebust nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
25057bfa891STrond Myklebust {
251d25be546STrond Myklebust 	struct nfs_delegation *ret = NULL;
252d25be546STrond Myklebust 	struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
25357bfa891STrond Myklebust 
25457bfa891STrond Myklebust 	if (delegation == NULL)
255d25be546STrond Myklebust 		goto out;
256d25be546STrond Myklebust 	spin_lock(&delegation->lock);
257d25be546STrond Myklebust 	if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
258d25be546STrond Myklebust 		ret = delegation;
259d25be546STrond Myklebust 	spin_unlock(&delegation->lock);
260d25be546STrond Myklebust out:
261d25be546STrond Myklebust 	return ret;
262d25be546STrond Myklebust }
263d25be546STrond Myklebust 
264d25be546STrond Myklebust static struct nfs_delegation *
265d25be546STrond Myklebust nfs_start_delegation_return(struct nfs_inode *nfsi)
266d25be546STrond Myklebust {
267d25be546STrond Myklebust 	struct nfs_delegation *delegation;
268d25be546STrond Myklebust 
269d25be546STrond Myklebust 	rcu_read_lock();
270d25be546STrond Myklebust 	delegation = nfs_start_delegation_return_locked(nfsi);
271d25be546STrond Myklebust 	rcu_read_unlock();
272d25be546STrond Myklebust 	return delegation;
273d25be546STrond Myklebust }
274d25be546STrond Myklebust 
275d25be546STrond Myklebust static void
276d25be546STrond Myklebust nfs_abort_delegation_return(struct nfs_delegation *delegation,
277d25be546STrond Myklebust 		struct nfs_client *clp)
278d25be546STrond Myklebust {
279dda4b225SChuck Lever 
28034310430STrond Myklebust 	spin_lock(&delegation->lock);
281d25be546STrond Myklebust 	clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
282d25be546STrond Myklebust 	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
283d25be546STrond Myklebust 	spin_unlock(&delegation->lock);
284d25be546STrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
285d25be546STrond Myklebust }
286d25be546STrond Myklebust 
287d25be546STrond Myklebust static struct nfs_delegation *
288d25be546STrond Myklebust nfs_detach_delegation_locked(struct nfs_inode *nfsi,
289d25be546STrond Myklebust 		struct nfs_delegation *delegation,
290d25be546STrond Myklebust 		struct nfs_client *clp)
291d25be546STrond Myklebust {
292d25be546STrond Myklebust 	struct nfs_delegation *deleg_cur =
293d25be546STrond Myklebust 		rcu_dereference_protected(nfsi->delegation,
294d25be546STrond Myklebust 				lockdep_is_held(&clp->cl_lock));
295d25be546STrond Myklebust 
296d25be546STrond Myklebust 	if (deleg_cur == NULL || delegation != deleg_cur)
297d25be546STrond Myklebust 		return NULL;
298d25be546STrond Myklebust 
299d25be546STrond Myklebust 	spin_lock(&delegation->lock);
300f9e0cc9cSTrond Myklebust 	if (!delegation->inode) {
301f9e0cc9cSTrond Myklebust 		spin_unlock(&delegation->lock);
302f9e0cc9cSTrond Myklebust 		return NULL;
303f9e0cc9cSTrond Myklebust 	}
30457bfa891STrond Myklebust 	list_del_rcu(&delegation->super_list);
30586e89489STrond Myklebust 	delegation->inode = NULL;
30657bfa891STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, NULL);
30734310430STrond Myklebust 	spin_unlock(&delegation->lock);
30857bfa891STrond Myklebust 	return delegation;
30957bfa891STrond Myklebust }
31057bfa891STrond Myklebust 
311dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
312d25be546STrond Myklebust 		struct nfs_delegation *delegation,
313d3978bb3SChuck Lever 		struct nfs_server *server)
314dda4b225SChuck Lever {
315d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
316dda4b225SChuck Lever 
317dda4b225SChuck Lever 	spin_lock(&clp->cl_lock);
318d25be546STrond Myklebust 	delegation = nfs_detach_delegation_locked(nfsi, delegation, clp);
319dda4b225SChuck Lever 	spin_unlock(&clp->cl_lock);
320dda4b225SChuck Lever 	return delegation;
321dda4b225SChuck Lever }
322dda4b225SChuck Lever 
323d25be546STrond Myklebust static struct nfs_delegation *
324d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode)
325d25be546STrond Myklebust {
326d25be546STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
327d25be546STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
328d25be546STrond Myklebust 	struct nfs_delegation *delegation;
329d25be546STrond Myklebust 
330ee05f456STrond Myklebust 	rcu_read_lock();
331ee05f456STrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
332ee05f456STrond Myklebust 	if (delegation != NULL)
333ee05f456STrond Myklebust 		delegation = nfs_detach_delegation(nfsi, delegation, server);
334ee05f456STrond Myklebust 	rcu_read_unlock();
335ee05f456STrond Myklebust 	return delegation;
336d25be546STrond Myklebust }
337d25be546STrond Myklebust 
338cf6726e2STrond Myklebust static void
339cf6726e2STrond Myklebust nfs_update_inplace_delegation(struct nfs_delegation *delegation,
340cf6726e2STrond Myklebust 		const struct nfs_delegation *update)
341cf6726e2STrond Myklebust {
342cf6726e2STrond Myklebust 	if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) {
343cf6726e2STrond Myklebust 		delegation->stateid.seqid = update->stateid.seqid;
344cf6726e2STrond Myklebust 		smp_wmb();
345cf6726e2STrond Myklebust 		delegation->type = update->type;
346ae084a32STrond Myklebust 		clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
347cf6726e2STrond Myklebust 	}
348cf6726e2STrond Myklebust }
349cf6726e2STrond Myklebust 
350d3978bb3SChuck Lever /**
351d3978bb3SChuck Lever  * nfs_inode_set_delegation - set up a delegation on an inode
352d3978bb3SChuck Lever  * @inode: inode to which delegation applies
353d3978bb3SChuck Lever  * @cred: cred to use for subsequent delegation processing
35435156bffSTrond Myklebust  * @type: delegation type
35535156bffSTrond Myklebust  * @stateid: delegation stateid
35635156bffSTrond Myklebust  * @pagemod_limit: write delegation "space_limit"
357d3978bb3SChuck Lever  *
358d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
3591da177e4SLinus Torvalds  */
360a52458b4SNeilBrown int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
36135156bffSTrond Myklebust 				  fmode_t type,
36235156bffSTrond Myklebust 				  const nfs4_stateid *stateid,
36335156bffSTrond Myklebust 				  unsigned long pagemod_limit)
3641da177e4SLinus Torvalds {
365d3978bb3SChuck Lever 	struct nfs_server *server = NFS_SERVER(inode);
366d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
3671da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
36817d2c0a0SDavid Howells 	struct nfs_delegation *delegation, *old_delegation;
36957bfa891STrond Myklebust 	struct nfs_delegation *freeme = NULL;
3701da177e4SLinus Torvalds 	int status = 0;
3711da177e4SLinus Torvalds 
3728535b2beSTrond Myklebust 	delegation = kmalloc(sizeof(*delegation), GFP_NOFS);
3731da177e4SLinus Torvalds 	if (delegation == NULL)
3741da177e4SLinus Torvalds 		return -ENOMEM;
37535156bffSTrond Myklebust 	nfs4_stateid_copy(&delegation->stateid, stateid);
37635156bffSTrond Myklebust 	delegation->type = type;
37735156bffSTrond Myklebust 	delegation->pagemod_limit = pagemod_limit;
3781eb5d98fSJeff Layton 	delegation->change_attr = inode_peek_iversion_raw(inode);
379a52458b4SNeilBrown 	delegation->cred = get_cred(cred);
3801da177e4SLinus Torvalds 	delegation->inode = inode;
381b7391f44STrond Myklebust 	delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
38234310430STrond Myklebust 	spin_lock_init(&delegation->lock);
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
38517d2c0a0SDavid Howells 	old_delegation = rcu_dereference_protected(nfsi->delegation,
38617d2c0a0SDavid Howells 					lockdep_is_held(&clp->cl_lock));
387ee05f456STrond Myklebust 	if (old_delegation == NULL)
388ee05f456STrond Myklebust 		goto add_new;
389cf6726e2STrond Myklebust 	/* Is this an update of the existing delegation? */
390cf6726e2STrond Myklebust 	if (nfs4_stateid_match_other(&old_delegation->stateid,
391cf6726e2STrond Myklebust 				&delegation->stateid)) {
392e0f07896STrond Myklebust 		spin_lock(&old_delegation->lock);
393cf6726e2STrond Myklebust 		nfs_update_inplace_delegation(old_delegation,
394cf6726e2STrond Myklebust 				delegation);
395e0f07896STrond Myklebust 		spin_unlock(&old_delegation->lock);
39657bfa891STrond Myklebust 		goto out;
39757bfa891STrond Myklebust 	}
398ee05f456STrond Myklebust 	if (!test_bit(NFS_DELEGATION_REVOKED, &old_delegation->flags)) {
39957bfa891STrond Myklebust 		/*
40057bfa891STrond Myklebust 		 * Deal with broken servers that hand out two
40157bfa891STrond Myklebust 		 * delegations for the same file.
40217280175STrond Myklebust 		 * Allow for upgrades to a WRITE delegation, but
40317280175STrond Myklebust 		 * nothing else.
40457bfa891STrond Myklebust 		 */
40557bfa891STrond Myklebust 		dfprintk(FILE, "%s: server %s handed out "
40657bfa891STrond Myklebust 				"a duplicate delegation!\n",
4073110ff80SHarvey Harrison 				__func__, clp->cl_hostname);
40817280175STrond Myklebust 		if (delegation->type == old_delegation->type ||
40917280175STrond Myklebust 		    !(delegation->type & FMODE_WRITE)) {
41057bfa891STrond Myklebust 			freeme = delegation;
41157bfa891STrond Myklebust 			delegation = NULL;
41257bfa891STrond Myklebust 			goto out;
41357bfa891STrond Myklebust 		}
414ade04647STrond Myklebust 		if (test_and_set_bit(NFS_DELEGATION_RETURNING,
415ade04647STrond Myklebust 					&old_delegation->flags))
416ade04647STrond Myklebust 			goto out;
417ee05f456STrond Myklebust 	}
418ee05f456STrond Myklebust 	freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp);
419d25be546STrond Myklebust 	if (freeme == NULL)
420d25be546STrond Myklebust 		goto out;
421ee05f456STrond Myklebust add_new:
42238942ba2STrond Myklebust 	list_add_tail_rcu(&delegation->super_list, &server->delegations);
4238383e460STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, delegation);
4241da177e4SLinus Torvalds 	delegation = NULL;
425412c77ceSTrond Myklebust 
42635156bffSTrond Myklebust 	trace_nfs4_set_delegation(inode, type);
427412c77ceSTrond Myklebust 
42897c2c17aSTrond Myklebust 	spin_lock(&inode->i_lock);
42997c2c17aSTrond Myklebust 	if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME))
43097c2c17aSTrond Myklebust 		NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED;
43197c2c17aSTrond Myklebust 	spin_unlock(&inode->i_lock);
43257bfa891STrond Myklebust out:
4331da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
434603c83daSTrond Myklebust 	if (delegation != NULL)
435603c83daSTrond Myklebust 		nfs_free_delegation(delegation);
436ee05f456STrond Myklebust 	if (freeme != NULL) {
43757bfa891STrond Myklebust 		nfs_do_return_delegation(inode, freeme, 0);
438ee05f456STrond Myklebust 		nfs_free_delegation(freeme);
439ee05f456STrond Myklebust 	}
4401da177e4SLinus Torvalds 	return status;
4411da177e4SLinus Torvalds }
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds /*
4441da177e4SLinus Torvalds  * Basic procedure for returning a delegation to the server
4451da177e4SLinus Torvalds  */
446d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
4471da177e4SLinus Torvalds {
448d25be546STrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
449869f9dfaSTrond Myklebust 	int err = 0;
4501da177e4SLinus Torvalds 
451d25be546STrond Myklebust 	if (delegation == NULL)
452d25be546STrond Myklebust 		return 0;
453d25be546STrond Myklebust 	do {
454869f9dfaSTrond Myklebust 		if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
455869f9dfaSTrond Myklebust 			break;
45624311f88STrond Myklebust 		err = nfs_delegation_claim_opens(inode, &delegation->stateid,
45724311f88STrond Myklebust 				delegation->type);
458d25be546STrond Myklebust 		if (!issync || err != -EAGAIN)
459d25be546STrond Myklebust 			break;
460d25be546STrond Myklebust 		/*
461d25be546STrond Myklebust 		 * Guard against state recovery
462d25be546STrond Myklebust 		 */
463d25be546STrond Myklebust 		err = nfs4_wait_clnt_recover(clp);
464d25be546STrond Myklebust 	} while (err == 0);
465d25be546STrond Myklebust 
466d25be546STrond Myklebust 	if (err) {
467d25be546STrond Myklebust 		nfs_abort_delegation_return(delegation, clp);
468d25be546STrond Myklebust 		goto out;
469d25be546STrond Myklebust 	}
4701da177e4SLinus Torvalds 
471d18cc1fdSTrond Myklebust 	err = nfs_do_return_delegation(inode, delegation, issync);
472d18cc1fdSTrond Myklebust out:
473d18cc1fdSTrond Myklebust 	return err;
47490163027STrond Myklebust }
47590163027STrond Myklebust 
476b757144fSTrond Myklebust static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
477b757144fSTrond Myklebust {
478b757144fSTrond Myklebust 	bool ret = false;
479b757144fSTrond Myklebust 
480b757144fSTrond Myklebust 	if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
481b757144fSTrond Myklebust 		ret = true;
4820d104167STrond Myklebust 	else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) {
483b757144fSTrond Myklebust 		struct inode *inode;
484b757144fSTrond Myklebust 
485b757144fSTrond Myklebust 		spin_lock(&delegation->lock);
486b757144fSTrond Myklebust 		inode = delegation->inode;
487b757144fSTrond Myklebust 		if (inode && list_empty(&NFS_I(inode)->open_files))
488b757144fSTrond Myklebust 			ret = true;
489b757144fSTrond Myklebust 		spin_unlock(&delegation->lock);
490b757144fSTrond Myklebust 	}
4910d104167STrond Myklebust 	if (ret)
4920d104167STrond Myklebust 		clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
493af20b7b8STrond Myklebust 	if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) ||
494af20b7b8STrond Myklebust 	    test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
495af20b7b8STrond Myklebust 		ret = false;
496af20b7b8STrond Myklebust 
497b757144fSTrond Myklebust 	return ret;
498b757144fSTrond Myklebust }
499b757144fSTrond Myklebust 
500d3978bb3SChuck Lever /**
501d3978bb3SChuck Lever  * nfs_client_return_marked_delegations - return previously marked delegations
502d3978bb3SChuck Lever  * @clp: nfs_client to process
503d3978bb3SChuck Lever  *
504dc327ed4STrond Myklebust  * Note that this function is designed to be called by the state
505dc327ed4STrond Myklebust  * manager thread. For this reason, it cannot flush the dirty data,
506dc327ed4STrond Myklebust  * since that could deadlock in case of a state recovery error.
507dc327ed4STrond Myklebust  *
508d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
509515d8611STrond Myklebust  */
510d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp)
511515d8611STrond Myklebust {
512515d8611STrond Myklebust 	struct nfs_delegation *delegation;
513e04bbf6bSNeilBrown 	struct nfs_delegation *prev;
514d3978bb3SChuck Lever 	struct nfs_server *server;
515515d8611STrond Myklebust 	struct inode *inode;
516e04bbf6bSNeilBrown 	struct inode *place_holder = NULL;
517e04bbf6bSNeilBrown 	struct nfs_delegation *place_holder_deleg = NULL;
518d18cc1fdSTrond Myklebust 	int err = 0;
519515d8611STrond Myklebust 
520515d8611STrond Myklebust restart:
521e04bbf6bSNeilBrown 	/*
522e04bbf6bSNeilBrown 	 * To avoid quadratic looping we hold a reference
523e04bbf6bSNeilBrown 	 * to an inode place_holder.  Each time we restart, we
524e04bbf6bSNeilBrown 	 * list nfs_servers from the server of that inode, and
525e04bbf6bSNeilBrown 	 * delegation in the server from the delegations of that
526e04bbf6bSNeilBrown 	 * inode.
527e04bbf6bSNeilBrown 	 * prev is an RCU-protected pointer to a delegation which
528e04bbf6bSNeilBrown 	 * wasn't marked for return and might be a good choice for
529e04bbf6bSNeilBrown 	 * the next place_holder.
530e04bbf6bSNeilBrown 	 */
531515d8611STrond Myklebust 	rcu_read_lock();
532e04bbf6bSNeilBrown 	prev = NULL;
533e04bbf6bSNeilBrown 	if (place_holder)
534e04bbf6bSNeilBrown 		server = NFS_SERVER(place_holder);
535e04bbf6bSNeilBrown 	else
536e04bbf6bSNeilBrown 		server = list_entry_rcu(clp->cl_superblocks.next,
537e04bbf6bSNeilBrown 					struct nfs_server, client_link);
538e04bbf6bSNeilBrown 	list_for_each_entry_from_rcu(server, &clp->cl_superblocks, client_link) {
539e04bbf6bSNeilBrown 		delegation = NULL;
540e04bbf6bSNeilBrown 		if (place_holder && server == NFS_SERVER(place_holder))
541e04bbf6bSNeilBrown 			delegation = rcu_dereference(NFS_I(place_holder)->delegation);
542e04bbf6bSNeilBrown 		if (!delegation || delegation != place_holder_deleg)
543e04bbf6bSNeilBrown 			delegation = list_entry_rcu(server->delegations.next,
544e04bbf6bSNeilBrown 						    struct nfs_delegation, super_list);
545e04bbf6bSNeilBrown 		list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) {
546e04bbf6bSNeilBrown 			struct inode *to_put = NULL;
547e04bbf6bSNeilBrown 
548e04bbf6bSNeilBrown 			if (!nfs_delegation_need_return(delegation)) {
549e04bbf6bSNeilBrown 				prev = delegation;
550515d8611STrond Myklebust 				continue;
551e04bbf6bSNeilBrown 			}
5529f0f8e12STrond Myklebust 			if (!nfs_sb_active(server->super))
553f3893491SNeilBrown 				break; /* continue in outer loop */
554e04bbf6bSNeilBrown 
555e04bbf6bSNeilBrown 			if (prev) {
556e04bbf6bSNeilBrown 				struct inode *tmp;
557e04bbf6bSNeilBrown 
558e04bbf6bSNeilBrown 				tmp = nfs_delegation_grab_inode(prev);
559e04bbf6bSNeilBrown 				if (tmp) {
560e04bbf6bSNeilBrown 					to_put = place_holder;
561e04bbf6bSNeilBrown 					place_holder = tmp;
562e04bbf6bSNeilBrown 					place_holder_deleg = prev;
563e04bbf6bSNeilBrown 				}
564e04bbf6bSNeilBrown 			}
565e04bbf6bSNeilBrown 
5669f0f8e12STrond Myklebust 			inode = nfs_delegation_grab_inode(delegation);
5679f0f8e12STrond Myklebust 			if (inode == NULL) {
5689f0f8e12STrond Myklebust 				rcu_read_unlock();
569e04bbf6bSNeilBrown 				if (to_put)
570e04bbf6bSNeilBrown 					iput(to_put);
5719f0f8e12STrond Myklebust 				nfs_sb_deactive(server->super);
5729f0f8e12STrond Myklebust 				goto restart;
5739f0f8e12STrond Myklebust 			}
574d25be546STrond Myklebust 			delegation = nfs_start_delegation_return_locked(NFS_I(inode));
575515d8611STrond Myklebust 			rcu_read_unlock();
576d3978bb3SChuck Lever 
577e04bbf6bSNeilBrown 			if (to_put)
578e04bbf6bSNeilBrown 				iput(to_put);
579e04bbf6bSNeilBrown 
580d25be546STrond Myklebust 			err = nfs_end_delegation_return(inode, delegation, 0);
581515d8611STrond Myklebust 			iput(inode);
5829f0f8e12STrond Myklebust 			nfs_sb_deactive(server->super);
5833ca951b6SNeilBrown 			cond_resched();
584d18cc1fdSTrond Myklebust 			if (!err)
585515d8611STrond Myklebust 				goto restart;
586d18cc1fdSTrond Myklebust 			set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
587e04bbf6bSNeilBrown 			if (place_holder)
588e04bbf6bSNeilBrown 				iput(place_holder);
589d18cc1fdSTrond Myklebust 			return err;
590515d8611STrond Myklebust 		}
591d3978bb3SChuck Lever 	}
592515d8611STrond Myklebust 	rcu_read_unlock();
593e04bbf6bSNeilBrown 	if (place_holder)
594e04bbf6bSNeilBrown 		iput(place_holder);
595d18cc1fdSTrond Myklebust 	return 0;
596515d8611STrond Myklebust }
597515d8611STrond Myklebust 
598d3978bb3SChuck Lever /**
599b47e0e47STrond Myklebust  * nfs_inode_evict_delegation - return delegation, don't reclaim opens
600d3978bb3SChuck Lever  * @inode: inode to process
601d3978bb3SChuck Lever  *
602d3978bb3SChuck Lever  * Does not protect against delegation reclaims, therefore really only safe
603b47e0e47STrond Myklebust  * to be called from nfs4_clear_inode(). Guaranteed to always free
604b47e0e47STrond Myklebust  * the delegation structure.
605e6f81075STrond Myklebust  */
606b47e0e47STrond Myklebust void nfs_inode_evict_delegation(struct inode *inode)
607e6f81075STrond Myklebust {
608e6f81075STrond Myklebust 	struct nfs_delegation *delegation;
609e6f81075STrond Myklebust 
610d25be546STrond Myklebust 	delegation = nfs_inode_detach_delegation(inode);
611b47e0e47STrond Myklebust 	if (delegation != NULL) {
612f885ea64STrond Myklebust 		set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
613b47e0e47STrond Myklebust 		set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
6145fcdfaccSTrond Myklebust 		nfs_do_return_delegation(inode, delegation, 1);
615ee05f456STrond Myklebust 		nfs_free_delegation(delegation);
616e6f81075STrond Myklebust 	}
617b47e0e47STrond Myklebust }
618e6f81075STrond Myklebust 
619d3978bb3SChuck Lever /**
620d3978bb3SChuck Lever  * nfs_inode_return_delegation - synchronously return a delegation
621d3978bb3SChuck Lever  * @inode: inode to process
622d3978bb3SChuck Lever  *
623c57d1bc5STrond Myklebust  * This routine will always flush any dirty data to disk on the
624c57d1bc5STrond Myklebust  * assumption that if we need to return the delegation, then
625c57d1bc5STrond Myklebust  * we should stop caching.
626c57d1bc5STrond Myklebust  *
627d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
628d3978bb3SChuck Lever  */
62957ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode)
63090163027STrond Myklebust {
63190163027STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
63290163027STrond Myklebust 	struct nfs_delegation *delegation;
63390163027STrond Myklebust 	int err = 0;
63490163027STrond Myklebust 
635c57d1bc5STrond Myklebust 	nfs_wb_all(inode);
636d25be546STrond Myklebust 	delegation = nfs_start_delegation_return(nfsi);
637d25be546STrond Myklebust 	if (delegation != NULL)
638d25be546STrond Myklebust 		err = nfs_end_delegation_return(inode, delegation, 1);
63990163027STrond Myklebust 	return err;
6401da177e4SLinus Torvalds }
6411da177e4SLinus Torvalds 
642c01d3645STrond Myklebust /**
643b7b7dac6STrond Myklebust  * nfs_inode_return_delegation_on_close - asynchronously return a delegation
644b7b7dac6STrond Myklebust  * @inode: inode to process
645b7b7dac6STrond Myklebust  *
646b7b7dac6STrond Myklebust  * This routine is called on file close in order to determine if the
647b7b7dac6STrond Myklebust  * inode delegation needs to be returned immediately.
648b7b7dac6STrond Myklebust  */
649b7b7dac6STrond Myklebust void nfs4_inode_return_delegation_on_close(struct inode *inode)
650b7b7dac6STrond Myklebust {
651b7b7dac6STrond Myklebust 	struct nfs_delegation *delegation;
652b7b7dac6STrond Myklebust 	struct nfs_delegation *ret = NULL;
653b7b7dac6STrond Myklebust 
654b7b7dac6STrond Myklebust 	if (!inode)
655b7b7dac6STrond Myklebust 		return;
656b7b7dac6STrond Myklebust 	rcu_read_lock();
657b7b7dac6STrond Myklebust 	delegation = nfs4_get_valid_delegation(inode);
658b7b7dac6STrond Myklebust 	if (!delegation)
659b7b7dac6STrond Myklebust 		goto out;
660b7b7dac6STrond Myklebust 	if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) {
661b7b7dac6STrond Myklebust 		spin_lock(&delegation->lock);
662b7b7dac6STrond Myklebust 		if (delegation->inode &&
663b7b7dac6STrond Myklebust 		    list_empty(&NFS_I(inode)->open_files) &&
664b7b7dac6STrond Myklebust 		    !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
665b7b7dac6STrond Myklebust 			clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
666b7b7dac6STrond Myklebust 			ret = delegation;
667b7b7dac6STrond Myklebust 		}
668b7b7dac6STrond Myklebust 		spin_unlock(&delegation->lock);
669b7b7dac6STrond Myklebust 	}
670b7b7dac6STrond Myklebust out:
671b7b7dac6STrond Myklebust 	rcu_read_unlock();
672b7b7dac6STrond Myklebust 	nfs_end_delegation_return(inode, ret, 0);
673b7b7dac6STrond Myklebust }
674b7b7dac6STrond Myklebust 
675b7b7dac6STrond Myklebust /**
676c01d3645STrond Myklebust  * nfs4_inode_make_writeable
677c01d3645STrond Myklebust  * @inode: pointer to inode
678c01d3645STrond Myklebust  *
679c01d3645STrond Myklebust  * Make the inode writeable by returning the delegation if necessary
680c01d3645STrond Myklebust  *
681c01d3645STrond Myklebust  * Returns zero on success, or a negative errno value.
682c01d3645STrond Myklebust  */
683c01d3645STrond Myklebust int nfs4_inode_make_writeable(struct inode *inode)
684c01d3645STrond Myklebust {
6853887ce1aSTrond Myklebust 	struct nfs_delegation *delegation;
6863887ce1aSTrond Myklebust 
6873887ce1aSTrond Myklebust 	rcu_read_lock();
6883887ce1aSTrond Myklebust 	delegation = nfs4_get_valid_delegation(inode);
6893887ce1aSTrond Myklebust 	if (delegation == NULL ||
6903887ce1aSTrond Myklebust 	    (nfs4_has_session(NFS_SERVER(inode)->nfs_client) &&
6913887ce1aSTrond Myklebust 	     (delegation->type & FMODE_WRITE))) {
6923887ce1aSTrond Myklebust 		rcu_read_unlock();
693c01d3645STrond Myklebust 		return 0;
694c01d3645STrond Myklebust 	}
6953887ce1aSTrond Myklebust 	rcu_read_unlock();
6963887ce1aSTrond Myklebust 	return nfs4_inode_return_delegation(inode);
6973887ce1aSTrond Myklebust }
698c01d3645STrond Myklebust 
699b757144fSTrond Myklebust static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
700b757144fSTrond Myklebust 		struct nfs_delegation *delegation)
701b757144fSTrond Myklebust {
702b757144fSTrond Myklebust 	set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
703b757144fSTrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
704b757144fSTrond Myklebust }
705b757144fSTrond Myklebust 
706ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server,
707ed1e6211STrond Myklebust 		struct nfs_delegation *delegation)
7086411bd4aSTrond Myklebust {
7096411bd4aSTrond Myklebust 	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
710ed1e6211STrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
7116411bd4aSTrond Myklebust }
7126411bd4aSTrond Myklebust 
7135c31e236STrond Myklebust static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
7145c31e236STrond Myklebust {
7155c31e236STrond Myklebust 	struct nfs_delegation *delegation;
7165c31e236STrond Myklebust 	bool ret = false;
7175c31e236STrond Myklebust 
7185c31e236STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
7195c31e236STrond Myklebust 		nfs_mark_return_delegation(server, delegation);
7205c31e236STrond Myklebust 		ret = true;
7215c31e236STrond Myklebust 	}
7225c31e236STrond Myklebust 	return ret;
7235c31e236STrond Myklebust }
7245c31e236STrond Myklebust 
725b02ba0b6STrond Myklebust static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
726b02ba0b6STrond Myklebust {
727b02ba0b6STrond Myklebust 	struct nfs_server *server;
728b02ba0b6STrond Myklebust 
729b02ba0b6STrond Myklebust 	rcu_read_lock();
730b02ba0b6STrond Myklebust 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
731b02ba0b6STrond Myklebust 		nfs_server_mark_return_all_delegations(server);
732b02ba0b6STrond Myklebust 	rcu_read_unlock();
733b02ba0b6STrond Myklebust }
734b02ba0b6STrond Myklebust 
735b02ba0b6STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp)
736b02ba0b6STrond Myklebust {
737b02ba0b6STrond Myklebust 	if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
738b02ba0b6STrond Myklebust 		nfs4_schedule_state_manager(clp);
739b02ba0b6STrond Myklebust }
740b02ba0b6STrond Myklebust 
741b02ba0b6STrond Myklebust /**
742b02ba0b6STrond Myklebust  * nfs_expire_all_delegations
743b02ba0b6STrond Myklebust  * @clp: client to process
744b02ba0b6STrond Myklebust  *
745b02ba0b6STrond Myklebust  */
746b02ba0b6STrond Myklebust void nfs_expire_all_delegations(struct nfs_client *clp)
747b02ba0b6STrond Myklebust {
748b02ba0b6STrond Myklebust 	nfs_client_mark_return_all_delegations(clp);
749b02ba0b6STrond Myklebust 	nfs_delegation_run_state_manager(clp);
750b02ba0b6STrond Myklebust }
751b02ba0b6STrond Myklebust 
752d3978bb3SChuck Lever /**
753d3978bb3SChuck Lever  * nfs_super_return_all_delegations - return delegations for one superblock
754302fad7bSTrond Myklebust  * @server: pointer to nfs_server to process
755d3978bb3SChuck Lever  *
7561da177e4SLinus Torvalds  */
757eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server)
7581da177e4SLinus Torvalds {
759d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
7605c31e236STrond Myklebust 	bool need_wait;
7611da177e4SLinus Torvalds 
7621da177e4SLinus Torvalds 	if (clp == NULL)
7631da177e4SLinus Torvalds 		return;
764d3978bb3SChuck Lever 
7658383e460STrond Myklebust 	rcu_read_lock();
7665c31e236STrond Myklebust 	need_wait = nfs_server_mark_return_all_delegations(server);
7678383e460STrond Myklebust 	rcu_read_unlock();
768d3978bb3SChuck Lever 
7695c31e236STrond Myklebust 	if (need_wait) {
770d18cc1fdSTrond Myklebust 		nfs4_schedule_state_manager(clp);
7715c31e236STrond Myklebust 		nfs4_wait_clnt_recover(clp);
7725c31e236STrond Myklebust 	}
773515d8611STrond Myklebust }
774515d8611STrond Myklebust 
775826e0013STrond Myklebust static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,
776d3978bb3SChuck Lever 						 fmode_t flags)
777515d8611STrond Myklebust {
778515d8611STrond Myklebust 	struct nfs_delegation *delegation;
779515d8611STrond Myklebust 
780d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
781c79571a5SAlexandros Batsakis 		if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
782c79571a5SAlexandros Batsakis 			continue;
783c79571a5SAlexandros Batsakis 		if (delegation->type & flags)
784826e0013STrond Myklebust 			nfs_mark_return_if_closed_delegation(server, delegation);
785707fb4b3STrond Myklebust 	}
786d3978bb3SChuck Lever }
787d3978bb3SChuck Lever 
788826e0013STrond Myklebust static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,
789d3978bb3SChuck Lever 							fmode_t flags)
790d3978bb3SChuck Lever {
791d3978bb3SChuck Lever 	struct nfs_server *server;
792d3978bb3SChuck Lever 
793d3978bb3SChuck Lever 	rcu_read_lock();
794d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
795826e0013STrond Myklebust 		nfs_mark_return_unused_delegation_types(server, flags);
796515d8611STrond Myklebust 	rcu_read_unlock();
7971da177e4SLinus Torvalds }
7981da177e4SLinus Torvalds 
79941020b67STrond Myklebust static void nfs_mark_delegation_revoked(struct nfs_server *server,
80041020b67STrond Myklebust 		struct nfs_delegation *delegation)
80141020b67STrond Myklebust {
80241020b67STrond Myklebust 	set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
803059b43e9STrond Myklebust 	delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
80441020b67STrond Myklebust }
80541020b67STrond Myklebust 
806ee05f456STrond Myklebust static void nfs_revoke_delegation(struct inode *inode,
80741020b67STrond Myklebust 		const nfs4_stateid *stateid)
808869f9dfaSTrond Myklebust {
809869f9dfaSTrond Myklebust 	struct nfs_delegation *delegation;
8107f048831STrond Myklebust 	nfs4_stateid tmp;
81141020b67STrond Myklebust 	bool ret = false;
81241020b67STrond Myklebust 
813869f9dfaSTrond Myklebust 	rcu_read_lock();
814869f9dfaSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
81541020b67STrond Myklebust 	if (delegation == NULL)
81641020b67STrond Myklebust 		goto out;
8177f048831STrond Myklebust 	if (stateid == NULL) {
8187f048831STrond Myklebust 		nfs4_stateid_copy(&tmp, &delegation->stateid);
8197f048831STrond Myklebust 		stateid = &tmp;
820f2d47b55STrond Myklebust 	} else {
821f2d47b55STrond Myklebust 		if (!nfs4_stateid_match_other(stateid, &delegation->stateid))
82241020b67STrond Myklebust 			goto out;
823f2d47b55STrond Myklebust 		spin_lock(&delegation->lock);
824f2d47b55STrond Myklebust 		if (stateid->seqid) {
825f2d47b55STrond Myklebust 			if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) {
826f2d47b55STrond Myklebust 				spin_unlock(&delegation->lock);
827f2d47b55STrond Myklebust 				goto out;
828f2d47b55STrond Myklebust 			}
829f2d47b55STrond Myklebust 			delegation->stateid.seqid = stateid->seqid;
830f2d47b55STrond Myklebust 		}
831f2d47b55STrond Myklebust 		spin_unlock(&delegation->lock);
832f2d47b55STrond Myklebust 	}
83341020b67STrond Myklebust 	nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
83441020b67STrond Myklebust 	ret = true;
83541020b67STrond Myklebust out:
836869f9dfaSTrond Myklebust 	rcu_read_unlock();
8377f048831STrond Myklebust 	if (ret)
8387f048831STrond Myklebust 		nfs_inode_find_state_and_recover(inode, stateid);
839869f9dfaSTrond Myklebust }
840869f9dfaSTrond Myklebust 
84141020b67STrond Myklebust void nfs_remove_bad_delegation(struct inode *inode,
84241020b67STrond Myklebust 		const nfs4_stateid *stateid)
843a1d0b5eeSTrond Myklebust {
844ee05f456STrond Myklebust 	nfs_revoke_delegation(inode, stateid);
845a1d0b5eeSTrond Myklebust }
8469cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
847a1d0b5eeSTrond Myklebust 
848d51f91d2STrond Myklebust void nfs_delegation_mark_returned(struct inode *inode,
849d51f91d2STrond Myklebust 		const nfs4_stateid *stateid)
850d51f91d2STrond Myklebust {
851d51f91d2STrond Myklebust 	struct nfs_delegation *delegation;
852d51f91d2STrond Myklebust 
853d51f91d2STrond Myklebust 	if (!inode)
854d51f91d2STrond Myklebust 		return;
855d51f91d2STrond Myklebust 
856d51f91d2STrond Myklebust 	rcu_read_lock();
857d51f91d2STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
858d51f91d2STrond Myklebust 	if (!delegation)
859d51f91d2STrond Myklebust 		goto out_rcu_unlock;
860d51f91d2STrond Myklebust 
861d51f91d2STrond Myklebust 	spin_lock(&delegation->lock);
862d51f91d2STrond Myklebust 	if (!nfs4_stateid_match_other(stateid, &delegation->stateid))
863d51f91d2STrond Myklebust 		goto out_spin_unlock;
864d51f91d2STrond Myklebust 	if (stateid->seqid) {
865d51f91d2STrond Myklebust 		/* If delegation->stateid is newer, dont mark as returned */
866d51f91d2STrond Myklebust 		if (nfs4_stateid_is_newer(&delegation->stateid, stateid))
867d51f91d2STrond Myklebust 			goto out_clear_returning;
868d51f91d2STrond Myklebust 		if (delegation->stateid.seqid != stateid->seqid)
869d51f91d2STrond Myklebust 			delegation->stateid.seqid = stateid->seqid;
870d51f91d2STrond Myklebust 	}
871d51f91d2STrond Myklebust 
872ee05f456STrond Myklebust 	nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
873d51f91d2STrond Myklebust 
874d51f91d2STrond Myklebust out_clear_returning:
875d51f91d2STrond Myklebust 	clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
876d51f91d2STrond Myklebust out_spin_unlock:
877d51f91d2STrond Myklebust 	spin_unlock(&delegation->lock);
878d51f91d2STrond Myklebust out_rcu_unlock:
879d51f91d2STrond Myklebust 	rcu_read_unlock();
880d51f91d2STrond Myklebust 
881d51f91d2STrond Myklebust 	nfs_inode_find_state_and_recover(inode, stateid);
882d51f91d2STrond Myklebust }
883d51f91d2STrond Myklebust 
884d3978bb3SChuck Lever /**
885826e0013STrond Myklebust  * nfs_expire_unused_delegation_types
886d3978bb3SChuck Lever  * @clp: client to process
887d3978bb3SChuck Lever  * @flags: delegation types to expire
888d3978bb3SChuck Lever  *
889d3978bb3SChuck Lever  */
890826e0013STrond Myklebust void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)
891c79571a5SAlexandros Batsakis {
892826e0013STrond Myklebust 	nfs_client_mark_return_unused_delegation_types(clp, flags);
893c79571a5SAlexandros Batsakis 	nfs_delegation_run_state_manager(clp);
894c79571a5SAlexandros Batsakis }
895c79571a5SAlexandros Batsakis 
896d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
897b7391f44STrond Myklebust {
898b7391f44STrond Myklebust 	struct nfs_delegation *delegation;
899b7391f44STrond Myklebust 
900d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
901b7391f44STrond Myklebust 		if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
902b7391f44STrond Myklebust 			continue;
903b757144fSTrond Myklebust 		nfs_mark_return_if_closed_delegation(server, delegation);
904b7391f44STrond Myklebust 	}
905b7391f44STrond Myklebust }
906b7391f44STrond Myklebust 
907d3978bb3SChuck Lever /**
908d3978bb3SChuck Lever  * nfs_expire_unreferenced_delegations - Eliminate unused delegations
909d3978bb3SChuck Lever  * @clp: nfs_client to process
910d3978bb3SChuck Lever  *
911d3978bb3SChuck Lever  */
912b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
913b7391f44STrond Myklebust {
914d3978bb3SChuck Lever 	struct nfs_server *server;
915d3978bb3SChuck Lever 
916d3978bb3SChuck Lever 	rcu_read_lock();
917d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
918d3978bb3SChuck Lever 		nfs_mark_return_unreferenced_delegations(server);
919d3978bb3SChuck Lever 	rcu_read_unlock();
920d3978bb3SChuck Lever 
921b7391f44STrond Myklebust 	nfs_delegation_run_state_manager(clp);
922b7391f44STrond Myklebust }
923b7391f44STrond Myklebust 
924d3978bb3SChuck Lever /**
925d3978bb3SChuck Lever  * nfs_async_inode_return_delegation - asynchronously return a delegation
926d3978bb3SChuck Lever  * @inode: inode to process
9278e663f0eSTrond Myklebust  * @stateid: state ID information
928d3978bb3SChuck Lever  *
929d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
9301da177e4SLinus Torvalds  */
931d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode,
932d3978bb3SChuck Lever 				      const nfs4_stateid *stateid)
9331da177e4SLinus Torvalds {
934ed1e6211STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
935ed1e6211STrond Myklebust 	struct nfs_client *clp = server->nfs_client;
9366411bd4aSTrond Myklebust 	struct nfs_delegation *delegation;
9371da177e4SLinus Torvalds 
9386411bd4aSTrond Myklebust 	rcu_read_lock();
939457a5042STrond Myklebust 	delegation = nfs4_get_valid_delegation(inode);
940755a48a7STrond Myklebust 	if (delegation == NULL)
941755a48a7STrond Myklebust 		goto out_enoent;
9424816fdadSTrond Myklebust 	if (stateid != NULL &&
9434816fdadSTrond Myklebust 	    !clp->cl_mvops->match_stateid(&delegation->stateid, stateid))
944755a48a7STrond Myklebust 		goto out_enoent;
945ed1e6211STrond Myklebust 	nfs_mark_return_delegation(server, delegation);
9466411bd4aSTrond Myklebust 	rcu_read_unlock();
947d3978bb3SChuck Lever 
9486411bd4aSTrond Myklebust 	nfs_delegation_run_state_manager(clp);
9496411bd4aSTrond Myklebust 	return 0;
950755a48a7STrond Myklebust out_enoent:
951755a48a7STrond Myklebust 	rcu_read_unlock();
952755a48a7STrond Myklebust 	return -ENOENT;
9531da177e4SLinus Torvalds }
9541da177e4SLinus Torvalds 
955d3978bb3SChuck Lever static struct inode *
956d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server,
957d3978bb3SChuck Lever 				 const struct nfs_fh *fhandle)
9581da177e4SLinus Torvalds {
9591da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
960e39d8a18STrond Myklebust 	struct inode *freeme, *res = NULL;
961d3978bb3SChuck Lever 
962d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
96386e89489STrond Myklebust 		spin_lock(&delegation->lock);
96486e89489STrond Myklebust 		if (delegation->inode != NULL &&
965457a5042STrond Myklebust 		    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
96686e89489STrond Myklebust 		    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
967e39d8a18STrond Myklebust 			freeme = igrab(delegation->inode);
968e39d8a18STrond Myklebust 			if (freeme && nfs_sb_active(freeme->i_sb))
969e39d8a18STrond Myklebust 				res = freeme;
97086e89489STrond Myklebust 			spin_unlock(&delegation->lock);
97186e89489STrond Myklebust 			if (res != NULL)
972d3978bb3SChuck Lever 				return res;
973e39d8a18STrond Myklebust 			if (freeme) {
974e39d8a18STrond Myklebust 				rcu_read_unlock();
975e39d8a18STrond Myklebust 				iput(freeme);
976e39d8a18STrond Myklebust 				rcu_read_lock();
977e39d8a18STrond Myklebust 			}
9786c342655STrond Myklebust 			return ERR_PTR(-EAGAIN);
9796c342655STrond Myklebust 		}
9806c342655STrond Myklebust 		spin_unlock(&delegation->lock);
9816c342655STrond Myklebust 	}
9826c342655STrond Myklebust 	return ERR_PTR(-ENOENT);
983d3978bb3SChuck Lever }
984d3978bb3SChuck Lever 
985d3978bb3SChuck Lever /**
986d3978bb3SChuck Lever  * nfs_delegation_find_inode - retrieve the inode associated with a delegation
987d3978bb3SChuck Lever  * @clp: client state handle
988d3978bb3SChuck Lever  * @fhandle: filehandle from a delegation recall
989d3978bb3SChuck Lever  *
990d3978bb3SChuck Lever  * Returns pointer to inode matching "fhandle," or NULL if a matching inode
991d3978bb3SChuck Lever  * cannot be found.
992d3978bb3SChuck Lever  */
993d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
994d3978bb3SChuck Lever 					const struct nfs_fh *fhandle)
995d3978bb3SChuck Lever {
996d3978bb3SChuck Lever 	struct nfs_server *server;
9976c342655STrond Myklebust 	struct inode *res;
998d3978bb3SChuck Lever 
999d3978bb3SChuck Lever 	rcu_read_lock();
1000d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
1001d3978bb3SChuck Lever 		res = nfs_delegation_find_inode_server(server, fhandle);
1002d5681f59SAnna Schumaker 		if (res != ERR_PTR(-ENOENT)) {
1003d5681f59SAnna Schumaker 			rcu_read_unlock();
10046c342655STrond Myklebust 			return res;
1005d3978bb3SChuck Lever 		}
1006d5681f59SAnna Schumaker 	}
10078383e460STrond Myklebust 	rcu_read_unlock();
10086c342655STrond Myklebust 	return ERR_PTR(-ENOENT);
10091da177e4SLinus Torvalds }
10101da177e4SLinus Torvalds 
1011d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
1012d3978bb3SChuck Lever {
1013d3978bb3SChuck Lever 	struct nfs_delegation *delegation;
1014d3978bb3SChuck Lever 
101545870d69STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
101645870d69STrond Myklebust 		/*
101745870d69STrond Myklebust 		 * If the delegation may have been admin revoked, then we
101845870d69STrond Myklebust 		 * cannot reclaim it.
101945870d69STrond Myklebust 		 */
102045870d69STrond Myklebust 		if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags))
102145870d69STrond Myklebust 			continue;
1022d3978bb3SChuck Lever 		set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
1023d3978bb3SChuck Lever 	}
102445870d69STrond Myklebust }
1025d3978bb3SChuck Lever 
1026d3978bb3SChuck Lever /**
1027d3978bb3SChuck Lever  * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed
1028d3978bb3SChuck Lever  * @clp: nfs_client to process
1029d3978bb3SChuck Lever  *
10301da177e4SLinus Torvalds  */
1031adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp)
10321da177e4SLinus Torvalds {
1033d3978bb3SChuck Lever 	struct nfs_server *server;
1034d3978bb3SChuck Lever 
10358383e460STrond Myklebust 	rcu_read_lock();
1036d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
1037d3978bb3SChuck Lever 		nfs_delegation_mark_reclaim_server(server);
10388383e460STrond Myklebust 	rcu_read_unlock();
10391da177e4SLinus Torvalds }
10401da177e4SLinus Torvalds 
1041d3978bb3SChuck Lever /**
1042d3978bb3SChuck Lever  * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done
1043d3978bb3SChuck Lever  * @clp: nfs_client to process
1044d3978bb3SChuck Lever  *
10451da177e4SLinus Torvalds  */
1046adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
10471da177e4SLinus Torvalds {
10488383e460STrond Myklebust 	struct nfs_delegation *delegation;
1049d3978bb3SChuck Lever 	struct nfs_server *server;
105086e89489STrond Myklebust 	struct inode *inode;
1051d3978bb3SChuck Lever 
10528383e460STrond Myklebust restart:
10538383e460STrond Myklebust 	rcu_read_lock();
1054d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
1055d3978bb3SChuck Lever 		list_for_each_entry_rcu(delegation, &server->delegations,
1056d3978bb3SChuck Lever 								super_list) {
10576f9449beSTrond Myklebust 			if (test_bit(NFS_DELEGATION_INODE_FREEING,
10586f9449beSTrond Myklebust 						&delegation->flags) ||
10596f9449beSTrond Myklebust 			    test_bit(NFS_DELEGATION_RETURNING,
10606f9449beSTrond Myklebust 						&delegation->flags) ||
10616f9449beSTrond Myklebust 			    test_bit(NFS_DELEGATION_NEED_RECLAIM,
1062d3978bb3SChuck Lever 						&delegation->flags) == 0)
10631da177e4SLinus Torvalds 				continue;
10649f0f8e12STrond Myklebust 			if (!nfs_sb_active(server->super))
1065f3893491SNeilBrown 				break; /* continue in outer loop */
10669f0f8e12STrond Myklebust 			inode = nfs_delegation_grab_inode(delegation);
10679f0f8e12STrond Myklebust 			if (inode == NULL) {
10689f0f8e12STrond Myklebust 				rcu_read_unlock();
10699f0f8e12STrond Myklebust 				nfs_sb_deactive(server->super);
10709f0f8e12STrond Myklebust 				goto restart;
10719f0f8e12STrond Myklebust 			}
1072b04b22f4STrond Myklebust 			delegation = nfs_start_delegation_return_locked(NFS_I(inode));
1073b04b22f4STrond Myklebust 			rcu_read_unlock();
1074b04b22f4STrond Myklebust 			if (delegation != NULL) {
1075d3978bb3SChuck Lever 				delegation = nfs_detach_delegation(NFS_I(inode),
1076d25be546STrond Myklebust 					delegation, server);
10778383e460STrond Myklebust 				if (delegation != NULL)
1078905f8d16STrond Myklebust 					nfs_free_delegation(delegation);
1079b04b22f4STrond Myklebust 			}
108086e89489STrond Myklebust 			iput(inode);
10819f0f8e12STrond Myklebust 			nfs_sb_deactive(server->super);
10823ca951b6SNeilBrown 			cond_resched();
10838383e460STrond Myklebust 			goto restart;
10841da177e4SLinus Torvalds 		}
1085d3978bb3SChuck Lever 	}
10868383e460STrond Myklebust 	rcu_read_unlock();
10871da177e4SLinus Torvalds }
10883e4f6290STrond Myklebust 
1089bb3d1a3bSTrond Myklebust static inline bool nfs4_server_rebooted(const struct nfs_client *clp)
1090bb3d1a3bSTrond Myklebust {
1091bb3d1a3bSTrond Myklebust 	return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) |
1092bb3d1a3bSTrond Myklebust 				BIT(NFS4CLNT_LEASE_EXPIRED) |
1093bb3d1a3bSTrond Myklebust 				BIT(NFS4CLNT_SESSION_RESET))) != 0;
1094bb3d1a3bSTrond Myklebust }
1095bb3d1a3bSTrond Myklebust 
109645870d69STrond Myklebust static void nfs_mark_test_expired_delegation(struct nfs_server *server,
109745870d69STrond Myklebust 	    struct nfs_delegation *delegation)
109845870d69STrond Myklebust {
1099059b43e9STrond Myklebust 	if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE)
1100059b43e9STrond Myklebust 		return;
110145870d69STrond Myklebust 	clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
110245870d69STrond Myklebust 	set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
110345870d69STrond Myklebust 	set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state);
110445870d69STrond Myklebust }
110545870d69STrond Myklebust 
1106bb3d1a3bSTrond Myklebust static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server,
1107bb3d1a3bSTrond Myklebust 		struct inode *inode)
1108bb3d1a3bSTrond Myklebust {
1109bb3d1a3bSTrond Myklebust 	struct nfs_delegation *delegation;
1110bb3d1a3bSTrond Myklebust 
1111bb3d1a3bSTrond Myklebust 	rcu_read_lock();
1112bb3d1a3bSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
1113bb3d1a3bSTrond Myklebust 	if (delegation)
1114bb3d1a3bSTrond Myklebust 		nfs_mark_test_expired_delegation(server, delegation);
1115bb3d1a3bSTrond Myklebust 	rcu_read_unlock();
1116bb3d1a3bSTrond Myklebust 
1117bb3d1a3bSTrond Myklebust }
1118bb3d1a3bSTrond Myklebust 
111945870d69STrond Myklebust static void nfs_delegation_mark_test_expired_server(struct nfs_server *server)
112045870d69STrond Myklebust {
112145870d69STrond Myklebust 	struct nfs_delegation *delegation;
112245870d69STrond Myklebust 
112345870d69STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list)
112445870d69STrond Myklebust 		nfs_mark_test_expired_delegation(server, delegation);
112545870d69STrond Myklebust }
112645870d69STrond Myklebust 
112745870d69STrond Myklebust /**
112845870d69STrond Myklebust  * nfs_mark_test_expired_all_delegations - mark all delegations for testing
112945870d69STrond Myklebust  * @clp: nfs_client to process
113045870d69STrond Myklebust  *
113145870d69STrond Myklebust  * Iterates through all the delegations associated with this server and
113245870d69STrond Myklebust  * marks them as needing to be checked for validity.
113345870d69STrond Myklebust  */
113445870d69STrond Myklebust void nfs_mark_test_expired_all_delegations(struct nfs_client *clp)
113545870d69STrond Myklebust {
113645870d69STrond Myklebust 	struct nfs_server *server;
113745870d69STrond Myklebust 
113845870d69STrond Myklebust 	rcu_read_lock();
113945870d69STrond Myklebust 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
114045870d69STrond Myklebust 		nfs_delegation_mark_test_expired_server(server);
114145870d69STrond Myklebust 	rcu_read_unlock();
114245870d69STrond Myklebust }
114345870d69STrond Myklebust 
114445870d69STrond Myklebust /**
11458ca017c8SScott Mayhew  * nfs_test_expired_all_delegations - test all delegations for a client
11468ca017c8SScott Mayhew  * @clp: nfs_client to process
11478ca017c8SScott Mayhew  *
11488ca017c8SScott Mayhew  * Helper for handling "recallable state revoked" status from server.
11498ca017c8SScott Mayhew  */
11508ca017c8SScott Mayhew void nfs_test_expired_all_delegations(struct nfs_client *clp)
11518ca017c8SScott Mayhew {
11528ca017c8SScott Mayhew 	nfs_mark_test_expired_all_delegations(clp);
11538ca017c8SScott Mayhew 	nfs4_schedule_state_manager(clp);
11548ca017c8SScott Mayhew }
11558ca017c8SScott Mayhew 
1156ad114089STrond Myklebust static void
1157ad114089STrond Myklebust nfs_delegation_test_free_expired(struct inode *inode,
1158ad114089STrond Myklebust 		nfs4_stateid *stateid,
1159ad114089STrond Myklebust 		const struct cred *cred)
1160ad114089STrond Myklebust {
1161ad114089STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
1162ad114089STrond Myklebust 	const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops;
1163ad114089STrond Myklebust 	int status;
1164ad114089STrond Myklebust 
1165ad114089STrond Myklebust 	if (!cred)
1166ad114089STrond Myklebust 		return;
1167ad114089STrond Myklebust 	status = ops->test_and_free_expired(server, stateid, cred);
1168ad114089STrond Myklebust 	if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID)
1169ad114089STrond Myklebust 		nfs_remove_bad_delegation(inode, stateid);
1170ad114089STrond Myklebust }
1171ad114089STrond Myklebust 
11728ca017c8SScott Mayhew /**
117345870d69STrond Myklebust  * nfs_reap_expired_delegations - reap expired delegations
117445870d69STrond Myklebust  * @clp: nfs_client to process
117545870d69STrond Myklebust  *
117645870d69STrond Myklebust  * Iterates through all the delegations associated with this server and
117745870d69STrond Myklebust  * checks if they have may have been revoked. This function is usually
117845870d69STrond Myklebust  * expected to be called in cases where the server may have lost its
117945870d69STrond Myklebust  * lease.
118045870d69STrond Myklebust  */
118145870d69STrond Myklebust void nfs_reap_expired_delegations(struct nfs_client *clp)
118245870d69STrond Myklebust {
118345870d69STrond Myklebust 	struct nfs_delegation *delegation;
118445870d69STrond Myklebust 	struct nfs_server *server;
118545870d69STrond Myklebust 	struct inode *inode;
1186a52458b4SNeilBrown 	const struct cred *cred;
118745870d69STrond Myklebust 	nfs4_stateid stateid;
118845870d69STrond Myklebust 
118945870d69STrond Myklebust restart:
119045870d69STrond Myklebust 	rcu_read_lock();
119145870d69STrond Myklebust 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
119245870d69STrond Myklebust 		list_for_each_entry_rcu(delegation, &server->delegations,
119345870d69STrond Myklebust 								super_list) {
11946f9449beSTrond Myklebust 			if (test_bit(NFS_DELEGATION_INODE_FREEING,
11956f9449beSTrond Myklebust 						&delegation->flags) ||
11966f9449beSTrond Myklebust 			    test_bit(NFS_DELEGATION_RETURNING,
11976f9449beSTrond Myklebust 						&delegation->flags) ||
11986f9449beSTrond Myklebust 			    test_bit(NFS_DELEGATION_TEST_EXPIRED,
119945870d69STrond Myklebust 						&delegation->flags) == 0)
120045870d69STrond Myklebust 				continue;
120145870d69STrond Myklebust 			if (!nfs_sb_active(server->super))
1202f3893491SNeilBrown 				break; /* continue in outer loop */
120345870d69STrond Myklebust 			inode = nfs_delegation_grab_inode(delegation);
120445870d69STrond Myklebust 			if (inode == NULL) {
120545870d69STrond Myklebust 				rcu_read_unlock();
120645870d69STrond Myklebust 				nfs_sb_deactive(server->super);
120745870d69STrond Myklebust 				goto restart;
120845870d69STrond Myklebust 			}
1209a52458b4SNeilBrown 			cred = get_cred_rcu(delegation->cred);
121045870d69STrond Myklebust 			nfs4_stateid_copy(&stateid, &delegation->stateid);
121145870d69STrond Myklebust 			clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
121245870d69STrond Myklebust 			rcu_read_unlock();
1213ad114089STrond Myklebust 			nfs_delegation_test_free_expired(inode, &stateid, cred);
1214a52458b4SNeilBrown 			put_cred(cred);
1215bb3d1a3bSTrond Myklebust 			if (nfs4_server_rebooted(clp)) {
1216bb3d1a3bSTrond Myklebust 				nfs_inode_mark_test_expired_delegation(server,inode);
1217bb3d1a3bSTrond Myklebust 				iput(inode);
1218bb3d1a3bSTrond Myklebust 				nfs_sb_deactive(server->super);
1219bb3d1a3bSTrond Myklebust 				return;
1220bb3d1a3bSTrond Myklebust 			}
122145870d69STrond Myklebust 			iput(inode);
122245870d69STrond Myklebust 			nfs_sb_deactive(server->super);
12233ca951b6SNeilBrown 			cond_resched();
122445870d69STrond Myklebust 			goto restart;
122545870d69STrond Myklebust 		}
122645870d69STrond Myklebust 	}
122745870d69STrond Myklebust 	rcu_read_unlock();
122845870d69STrond Myklebust }
122945870d69STrond Myklebust 
12306c2d8f8dSTrond Myklebust void nfs_inode_find_delegation_state_and_recover(struct inode *inode,
12316c2d8f8dSTrond Myklebust 		const nfs4_stateid *stateid)
12326c2d8f8dSTrond Myklebust {
12336c2d8f8dSTrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
12346c2d8f8dSTrond Myklebust 	struct nfs_delegation *delegation;
12356c2d8f8dSTrond Myklebust 	bool found = false;
12366c2d8f8dSTrond Myklebust 
12376c2d8f8dSTrond Myklebust 	rcu_read_lock();
12386c2d8f8dSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
12396c2d8f8dSTrond Myklebust 	if (delegation &&
124042c304c3STrond Myklebust 	    nfs4_stateid_match_or_older(&delegation->stateid, stateid) &&
124142c304c3STrond Myklebust 	    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
12426c2d8f8dSTrond Myklebust 		nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation);
12436c2d8f8dSTrond Myklebust 		found = true;
12446c2d8f8dSTrond Myklebust 	}
12456c2d8f8dSTrond Myklebust 	rcu_read_unlock();
12466c2d8f8dSTrond Myklebust 	if (found)
12476c2d8f8dSTrond Myklebust 		nfs4_schedule_state_manager(clp);
12486c2d8f8dSTrond Myklebust }
12496c2d8f8dSTrond Myklebust 
1250d3978bb3SChuck Lever /**
1251d3978bb3SChuck Lever  * nfs_delegations_present - check for existence of delegations
1252d3978bb3SChuck Lever  * @clp: client state handle
1253d3978bb3SChuck Lever  *
1254d3978bb3SChuck Lever  * Returns one if there are any nfs_delegation structures attached
1255d3978bb3SChuck Lever  * to this nfs_client.
1256d3978bb3SChuck Lever  */
1257d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp)
1258d3978bb3SChuck Lever {
1259d3978bb3SChuck Lever 	struct nfs_server *server;
1260d3978bb3SChuck Lever 	int ret = 0;
1261d3978bb3SChuck Lever 
1262d3978bb3SChuck Lever 	rcu_read_lock();
1263d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
1264d3978bb3SChuck Lever 		if (!list_empty(&server->delegations)) {
1265d3978bb3SChuck Lever 			ret = 1;
1266d3978bb3SChuck Lever 			break;
1267d3978bb3SChuck Lever 		}
1268d3978bb3SChuck Lever 	rcu_read_unlock();
1269d3978bb3SChuck Lever 	return ret;
1270d3978bb3SChuck Lever }
1271d3978bb3SChuck Lever 
1272d3978bb3SChuck Lever /**
127312f275cdSTrond Myklebust  * nfs4_refresh_delegation_stateid - Update delegation stateid seqid
127412f275cdSTrond Myklebust  * @dst: stateid to refresh
127512f275cdSTrond Myklebust  * @inode: inode to check
127612f275cdSTrond Myklebust  *
127712f275cdSTrond Myklebust  * Returns "true" and updates "dst->seqid" * if inode had a delegation
127812f275cdSTrond Myklebust  * that matches our delegation stateid. Otherwise "false" is returned.
127912f275cdSTrond Myklebust  */
128012f275cdSTrond Myklebust bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
128112f275cdSTrond Myklebust {
128212f275cdSTrond Myklebust 	struct nfs_delegation *delegation;
128312f275cdSTrond Myklebust 	bool ret = false;
128412f275cdSTrond Myklebust 	if (!inode)
128512f275cdSTrond Myklebust 		goto out;
128612f275cdSTrond Myklebust 
128712f275cdSTrond Myklebust 	rcu_read_lock();
128812f275cdSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
128912f275cdSTrond Myklebust 	if (delegation != NULL &&
1290b5756208STrond Myklebust 	    nfs4_stateid_match_other(dst, &delegation->stateid) &&
1291246afc0aSTrond Myklebust 	    nfs4_stateid_is_newer(&delegation->stateid, dst) &&
1292b5756208STrond Myklebust 	    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
129312f275cdSTrond Myklebust 		dst->seqid = delegation->stateid.seqid;
129479cc5542STrond Myklebust 		ret = true;
129512f275cdSTrond Myklebust 	}
129612f275cdSTrond Myklebust 	rcu_read_unlock();
129712f275cdSTrond Myklebust out:
129812f275cdSTrond Myklebust 	return ret;
129912f275cdSTrond Myklebust }
130012f275cdSTrond Myklebust 
130112f275cdSTrond Myklebust /**
1302d3978bb3SChuck Lever  * nfs4_copy_delegation_stateid - Copy inode's state ID information
1303d3978bb3SChuck Lever  * @inode: inode to check
13040032a7a7STrond Myklebust  * @flags: delegation type requirement
1305abf4e13cSTrond Myklebust  * @dst: stateid data structure to fill in
1306abf4e13cSTrond Myklebust  * @cred: optional argument to retrieve credential
1307d3978bb3SChuck Lever  *
13080032a7a7STrond Myklebust  * Returns "true" and fills in "dst->data" * if inode had a delegation,
13090032a7a7STrond Myklebust  * otherwise "false" is returned.
1310d3978bb3SChuck Lever  */
1311abf4e13cSTrond Myklebust bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags,
1312a52458b4SNeilBrown 		nfs4_stateid *dst, const struct cred **cred)
13133e4f6290STrond Myklebust {
13143e4f6290STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
13153e4f6290STrond Myklebust 	struct nfs_delegation *delegation;
13160032a7a7STrond Myklebust 	bool ret;
13173e4f6290STrond Myklebust 
13180032a7a7STrond Myklebust 	flags &= FMODE_READ|FMODE_WRITE;
13198383e460STrond Myklebust 	rcu_read_lock();
13208383e460STrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
1321aa05c87fSTrond Myklebust 	ret = nfs4_is_valid_delegation(delegation, flags);
13220032a7a7STrond Myklebust 	if (ret) {
1323f597c537STrond Myklebust 		nfs4_stateid_copy(dst, &delegation->stateid);
13240032a7a7STrond Myklebust 		nfs_mark_delegation_referenced(delegation);
1325abf4e13cSTrond Myklebust 		if (cred)
1326a52458b4SNeilBrown 			*cred = get_cred(delegation->cred);
13273e4f6290STrond Myklebust 	}
13288383e460STrond Myklebust 	rcu_read_unlock();
13298383e460STrond Myklebust 	return ret;
13303e4f6290STrond Myklebust }
13315445b1fbSTrond Myklebust 
13325445b1fbSTrond Myklebust /**
13335445b1fbSTrond Myklebust  * nfs4_delegation_flush_on_close - Check if we must flush file on close
13345445b1fbSTrond Myklebust  * @inode: inode to check
13355445b1fbSTrond Myklebust  *
13365445b1fbSTrond Myklebust  * This function checks the number of outstanding writes to the file
13375445b1fbSTrond Myklebust  * against the delegation 'space_limit' field to see if
13385445b1fbSTrond Myklebust  * the spec requires us to flush the file on close.
13395445b1fbSTrond Myklebust  */
13405445b1fbSTrond Myklebust bool nfs4_delegation_flush_on_close(const struct inode *inode)
13415445b1fbSTrond Myklebust {
13425445b1fbSTrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
13435445b1fbSTrond Myklebust 	struct nfs_delegation *delegation;
13445445b1fbSTrond Myklebust 	bool ret = true;
13455445b1fbSTrond Myklebust 
13465445b1fbSTrond Myklebust 	rcu_read_lock();
13475445b1fbSTrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
13485445b1fbSTrond Myklebust 	if (delegation == NULL || !(delegation->type & FMODE_WRITE))
13495445b1fbSTrond Myklebust 		goto out;
1350a6b6d5b8STrond Myklebust 	if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit)
13515445b1fbSTrond Myklebust 		ret = false;
13525445b1fbSTrond Myklebust out:
13535445b1fbSTrond Myklebust 	rcu_read_unlock();
13545445b1fbSTrond Myklebust 	return ret;
13555445b1fbSTrond Myklebust }
1356