xref: /openbmc/linux/fs/nfs/delegation.c (revision 17b985de)
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 
2810717f45STrond Myklebust #define NFS_DEFAULT_DELEGATION_WATERMARK (5000U)
2910717f45STrond Myklebust 
30d2269ea1STrond Myklebust static atomic_long_t nfs_active_delegations;
3110717f45STrond Myklebust static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
32d2269ea1STrond Myklebust 
__nfs_free_delegation(struct nfs_delegation * delegation)33d2269ea1STrond Myklebust static void __nfs_free_delegation(struct nfs_delegation *delegation)
34905f8d16STrond Myklebust {
35a52458b4SNeilBrown 	put_cred(delegation->cred);
36e00b8a24STrond Myklebust 	delegation->cred = NULL;
3726f04ddeSLai Jiangshan 	kfree_rcu(delegation, rcu);
388383e460STrond Myklebust }
398383e460STrond Myklebust 
nfs_mark_delegation_revoked(struct nfs_delegation * delegation)40d2269ea1STrond Myklebust static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation)
41d2269ea1STrond Myklebust {
42d2269ea1STrond Myklebust 	if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
43d2269ea1STrond Myklebust 		delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
44d2269ea1STrond Myklebust 		atomic_long_dec(&nfs_active_delegations);
45efeda80dSTrond Myklebust 		if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
46efeda80dSTrond Myklebust 			nfs_clear_verifier_delegated(delegation->inode);
47d2269ea1STrond Myklebust 	}
48d2269ea1STrond Myklebust }
49d2269ea1STrond Myklebust 
nfs_get_delegation(struct nfs_delegation * delegation)508c75593cSTrond Myklebust static struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegation)
518c75593cSTrond Myklebust {
528c75593cSTrond Myklebust 	refcount_inc(&delegation->refcount);
538c75593cSTrond Myklebust 	return delegation;
548c75593cSTrond Myklebust }
558c75593cSTrond Myklebust 
nfs_put_delegation(struct nfs_delegation * delegation)568c75593cSTrond Myklebust static void nfs_put_delegation(struct nfs_delegation *delegation)
578c75593cSTrond Myklebust {
588c75593cSTrond Myklebust 	if (refcount_dec_and_test(&delegation->refcount))
598c75593cSTrond Myklebust 		__nfs_free_delegation(delegation);
608c75593cSTrond Myklebust }
618c75593cSTrond Myklebust 
nfs_free_delegation(struct nfs_delegation * delegation)62d2269ea1STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation)
63d2269ea1STrond Myklebust {
64d2269ea1STrond Myklebust 	nfs_mark_delegation_revoked(delegation);
658c75593cSTrond Myklebust 	nfs_put_delegation(delegation);
66d2269ea1STrond Myklebust }
67d2269ea1STrond Myklebust 
68d3978bb3SChuck Lever /**
69d3978bb3SChuck Lever  * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
70d3978bb3SChuck Lever  * @delegation: delegation to process
71d3978bb3SChuck Lever  *
72d3978bb3SChuck Lever  */
nfs_mark_delegation_referenced(struct nfs_delegation * delegation)73b7391f44STrond Myklebust void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
74b7391f44STrond Myklebust {
75b7391f44STrond Myklebust 	set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
76b7391f44STrond Myklebust }
77b7391f44STrond Myklebust 
nfs_mark_return_delegation(struct nfs_server * server,struct nfs_delegation * delegation)78be200377STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server,
79be200377STrond Myklebust 				       struct nfs_delegation *delegation)
80be200377STrond Myklebust {
81be200377STrond Myklebust 	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
82be200377STrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
83be200377STrond Myklebust }
84be200377STrond Myklebust 
85aa05c87fSTrond Myklebust static bool
nfs4_is_valid_delegation(const struct nfs_delegation * delegation,fmode_t flags)86aa05c87fSTrond Myklebust nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
87aa05c87fSTrond Myklebust 		fmode_t flags)
88aa05c87fSTrond Myklebust {
89aa05c87fSTrond Myklebust 	if (delegation != NULL && (delegation->type & flags) == flags &&
90aa05c87fSTrond Myklebust 	    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
91aa05c87fSTrond Myklebust 	    !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
92aa05c87fSTrond Myklebust 		return true;
93aa05c87fSTrond Myklebust 	return false;
94aa05c87fSTrond Myklebust }
95aa05c87fSTrond Myklebust 
nfs4_get_valid_delegation(const struct inode * inode)96be3df3ddSTrond Myklebust struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode)
97be3df3ddSTrond Myklebust {
98be3df3ddSTrond Myklebust 	struct nfs_delegation *delegation;
99be3df3ddSTrond Myklebust 
100be3df3ddSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
101be3df3ddSTrond Myklebust 	if (nfs4_is_valid_delegation(delegation, 0))
102be3df3ddSTrond Myklebust 		return delegation;
103be3df3ddSTrond Myklebust 	return NULL;
104be3df3ddSTrond Myklebust }
105be3df3ddSTrond Myklebust 
10615bb3afeSPeng Tao static int
nfs4_do_check_delegation(struct inode * inode,fmode_t flags,bool mark)10715bb3afeSPeng Tao nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
108b7391f44STrond Myklebust {
109b7391f44STrond Myklebust 	struct nfs_delegation *delegation;
110b7391f44STrond Myklebust 	int ret = 0;
111b7391f44STrond Myklebust 
112b7391f44STrond Myklebust 	flags &= FMODE_READ|FMODE_WRITE;
113b7391f44STrond Myklebust 	rcu_read_lock();
114b7391f44STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
115aa05c87fSTrond Myklebust 	if (nfs4_is_valid_delegation(delegation, flags)) {
11615bb3afeSPeng Tao 		if (mark)
117b7391f44STrond Myklebust 			nfs_mark_delegation_referenced(delegation);
118b7391f44STrond Myklebust 		ret = 1;
119b7391f44STrond Myklebust 	}
120b7391f44STrond Myklebust 	rcu_read_unlock();
121b7391f44STrond Myklebust 	return ret;
122b7391f44STrond Myklebust }
12315bb3afeSPeng Tao /**
1246453bcd0STrond Myklebust  * nfs4_have_delegation - check if inode has a delegation, mark it
12515bb3afeSPeng Tao  * NFS_DELEGATION_REFERENCED if there is one.
12615bb3afeSPeng Tao  * @inode: inode to check
12715bb3afeSPeng Tao  * @flags: delegation types to check for
12815bb3afeSPeng Tao  *
12915bb3afeSPeng Tao  * Returns one if inode has the indicated delegation, otherwise zero.
13015bb3afeSPeng Tao  */
nfs4_have_delegation(struct inode * inode,fmode_t flags)13115bb3afeSPeng Tao int nfs4_have_delegation(struct inode *inode, fmode_t flags)
13215bb3afeSPeng Tao {
13315bb3afeSPeng Tao 	return nfs4_do_check_delegation(inode, flags, true);
13415bb3afeSPeng Tao }
13515bb3afeSPeng Tao 
13615bb3afeSPeng Tao /*
13715bb3afeSPeng Tao  * nfs4_check_delegation - check if inode has a delegation, do not mark
13815bb3afeSPeng Tao  * NFS_DELEGATION_REFERENCED if it has one.
13915bb3afeSPeng Tao  */
nfs4_check_delegation(struct inode * inode,fmode_t flags)14015bb3afeSPeng Tao int nfs4_check_delegation(struct inode *inode, fmode_t flags)
14115bb3afeSPeng Tao {
14215bb3afeSPeng Tao 	return nfs4_do_check_delegation(inode, flags, false);
14315bb3afeSPeng Tao }
144b7391f44STrond Myklebust 
nfs_delegation_claim_locks(struct nfs4_state * state,const nfs4_stateid * stateid)14544f411c3SOlga Kornievskaia static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid)
146888e694cSTrond Myklebust {
147888e694cSTrond Myklebust 	struct inode *inode = state->inode;
148888e694cSTrond Myklebust 	struct file_lock *fl;
149*17b985deSJeff Layton 	struct file_lock_context *flctx = locks_inode_context(inode);
150bd61e0a9SJeff Layton 	struct list_head *list;
151d5122201STrond Myklebust 	int status = 0;
152888e694cSTrond Myklebust 
153bd61e0a9SJeff Layton 	if (flctx == NULL)
15465b62a29STrond Myklebust 		goto out;
155314d7cc0SJeff Layton 
156bd61e0a9SJeff Layton 	list = &flctx->flc_posix;
1576109c850SJeff Layton 	spin_lock(&flctx->flc_lock);
158bd61e0a9SJeff Layton restart:
159bd61e0a9SJeff Layton 	list_for_each_entry(fl, list, fl_list) {
16044f411c3SOlga Kornievskaia 		if (nfs_file_open_context(fl->fl_file)->state != state)
161888e694cSTrond Myklebust 			continue;
1626109c850SJeff Layton 		spin_unlock(&flctx->flc_lock);
163db4f2e63STrond Myklebust 		status = nfs4_lock_delegation_recall(fl, state, stateid);
164d5122201STrond Myklebust 		if (status < 0)
1653f09df70STrond Myklebust 			goto out;
1666109c850SJeff Layton 		spin_lock(&flctx->flc_lock);
167888e694cSTrond Myklebust 	}
168bd61e0a9SJeff Layton 	if (list == &flctx->flc_posix) {
169bd61e0a9SJeff Layton 		list = &flctx->flc_flock;
170bd61e0a9SJeff Layton 		goto restart;
1715263e31eSJeff Layton 	}
1726109c850SJeff Layton 	spin_unlock(&flctx->flc_lock);
1733f09df70STrond Myklebust out:
174888e694cSTrond Myklebust 	return status;
175888e694cSTrond Myklebust }
176888e694cSTrond Myklebust 
nfs_delegation_claim_opens(struct inode * inode,const nfs4_stateid * stateid,fmode_t type)17724311f88STrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode,
17824311f88STrond Myklebust 		const nfs4_stateid *stateid, fmode_t type)
1791da177e4SLinus Torvalds {
1801da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
1811da177e4SLinus Torvalds 	struct nfs_open_context *ctx;
182d25be546STrond Myklebust 	struct nfs4_state_owner *sp;
1831da177e4SLinus Torvalds 	struct nfs4_state *state;
184d25be546STrond Myklebust 	unsigned int seq;
185888e694cSTrond Myklebust 	int err;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds again:
1880de43976STrond Myklebust 	rcu_read_lock();
1890de43976STrond Myklebust 	list_for_each_entry_rcu(ctx, &nfsi->open_files, list) {
1901da177e4SLinus Torvalds 		state = ctx->state;
1911da177e4SLinus Torvalds 		if (state == NULL)
1921da177e4SLinus Torvalds 			continue;
1931da177e4SLinus Torvalds 		if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
1941da177e4SLinus Torvalds 			continue;
195f8ebf7a8STrond Myklebust 		if (!nfs4_valid_open_stateid(state))
196f8ebf7a8STrond Myklebust 			continue;
197f597c537STrond Myklebust 		if (!nfs4_stateid_match(&state->stateid, stateid))
19890163027STrond Myklebust 			continue;
1990de43976STrond Myklebust 		if (!get_nfs_open_context(ctx))
2000de43976STrond Myklebust 			continue;
2010de43976STrond Myklebust 		rcu_read_unlock();
202d25be546STrond Myklebust 		sp = state->owner;
20365b62a29STrond Myklebust 		/* Block nfs4_proc_unlck */
20465b62a29STrond Myklebust 		mutex_lock(&sp->so_delegreturn_mutex);
205d25be546STrond Myklebust 		seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
2065eb8d18cSTrond Myklebust 		err = nfs4_open_delegation_recall(ctx, state, stateid);
207d25be546STrond Myklebust 		if (!err)
20844f411c3SOlga Kornievskaia 			err = nfs_delegation_claim_locks(state, stateid);
209d25be546STrond Myklebust 		if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
210d25be546STrond Myklebust 			err = -EAGAIN;
21165b62a29STrond Myklebust 		mutex_unlock(&sp->so_delegreturn_mutex);
2121da177e4SLinus Torvalds 		put_nfs_open_context(ctx);
213888e694cSTrond Myklebust 		if (err != 0)
214d18cc1fdSTrond Myklebust 			return err;
2151da177e4SLinus Torvalds 		goto again;
2161da177e4SLinus Torvalds 	}
2170de43976STrond Myklebust 	rcu_read_unlock();
218d18cc1fdSTrond Myklebust 	return 0;
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
221d3978bb3SChuck Lever /**
222d3978bb3SChuck Lever  * nfs_inode_reclaim_delegation - process a delegation reclaim request
223d3978bb3SChuck Lever  * @inode: inode to process
224d3978bb3SChuck Lever  * @cred: credential to use for request
22535156bffSTrond Myklebust  * @type: delegation type
22635156bffSTrond Myklebust  * @stateid: delegation stateid
22735156bffSTrond Myklebust  * @pagemod_limit: write delegation "space_limit"
228d3978bb3SChuck Lever  *
2291da177e4SLinus Torvalds  */
nfs_inode_reclaim_delegation(struct inode * inode,const struct cred * cred,fmode_t type,const nfs4_stateid * stateid,unsigned long pagemod_limit)230a52458b4SNeilBrown void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
2311ba04394STrond Myklebust 				  fmode_t type, const nfs4_stateid *stateid,
23235156bffSTrond Myklebust 				  unsigned long pagemod_limit)
2331da177e4SLinus Torvalds {
2348f649c37STrond Myklebust 	struct nfs_delegation *delegation;
235a52458b4SNeilBrown 	const struct cred *oldcred = NULL;
2361da177e4SLinus Torvalds 
2378f649c37STrond Myklebust 	rcu_read_lock();
2388f649c37STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
2398f649c37STrond Myklebust 	if (delegation != NULL) {
2408f649c37STrond Myklebust 		spin_lock(&delegation->lock);
24135156bffSTrond Myklebust 		nfs4_stateid_copy(&delegation->stateid, stateid);
24235156bffSTrond Myklebust 		delegation->type = type;
24335156bffSTrond Myklebust 		delegation->pagemod_limit = pagemod_limit;
24405c88babSTrond Myklebust 		oldcred = delegation->cred;
245a52458b4SNeilBrown 		delegation->cred = get_cred(cred);
2461ba04394STrond Myklebust 		clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
2471ba04394STrond Myklebust 		if (test_and_clear_bit(NFS_DELEGATION_REVOKED,
2481ba04394STrond Myklebust 				       &delegation->flags))
2491ba04394STrond Myklebust 			atomic_long_inc(&nfs_active_delegations);
2508f649c37STrond Myklebust 		spin_unlock(&delegation->lock);
2518f649c37STrond Myklebust 		rcu_read_unlock();
252a52458b4SNeilBrown 		put_cred(oldcred);
25335156bffSTrond Myklebust 		trace_nfs4_reclaim_delegation(inode, type);
2541ba04394STrond Myklebust 	} else {
2558f649c37STrond Myklebust 		rcu_read_unlock();
2561ba04394STrond Myklebust 		nfs_inode_set_delegation(inode, cred, type, stateid,
2571ba04394STrond Myklebust 					 pagemod_limit);
2581ba04394STrond Myklebust 	}
2598f649c37STrond Myklebust }
2601da177e4SLinus Torvalds 
nfs_do_return_delegation(struct inode * inode,struct nfs_delegation * delegation,int issync)26157bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
26257bfa891STrond Myklebust {
2635d63944fSTrond Myklebust 	const struct cred *cred;
26457bfa891STrond Myklebust 	int res = 0;
26557bfa891STrond Myklebust 
2665d63944fSTrond Myklebust 	if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
2675d63944fSTrond Myklebust 		spin_lock(&delegation->lock);
2685d63944fSTrond Myklebust 		cred = get_cred(delegation->cred);
2695d63944fSTrond Myklebust 		spin_unlock(&delegation->lock);
2705d63944fSTrond Myklebust 		res = nfs4_proc_delegreturn(inode, cred,
271869f9dfaSTrond Myklebust 				&delegation->stateid,
272869f9dfaSTrond Myklebust 				issync);
2735d63944fSTrond Myklebust 		put_cred(cred);
2745d63944fSTrond Myklebust 	}
27557bfa891STrond Myklebust 	return res;
27657bfa891STrond Myklebust }
27757bfa891STrond Myklebust 
nfs_delegation_grab_inode(struct nfs_delegation * delegation)27886e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
27986e89489STrond Myklebust {
28086e89489STrond Myklebust 	struct inode *inode = NULL;
28186e89489STrond Myklebust 
28286e89489STrond Myklebust 	spin_lock(&delegation->lock);
28386e89489STrond Myklebust 	if (delegation->inode != NULL)
28486e89489STrond Myklebust 		inode = igrab(delegation->inode);
2856f9449beSTrond Myklebust 	if (!inode)
2866f9449beSTrond Myklebust 		set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
28786e89489STrond Myklebust 	spin_unlock(&delegation->lock);
28886e89489STrond Myklebust 	return inode;
28986e89489STrond Myklebust }
29086e89489STrond Myklebust 
291dda4b225SChuck Lever static struct nfs_delegation *
nfs_start_delegation_return_locked(struct nfs_inode * nfsi)292d25be546STrond Myklebust nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
29357bfa891STrond Myklebust {
294d25be546STrond Myklebust 	struct nfs_delegation *ret = NULL;
295d25be546STrond Myklebust 	struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
29657bfa891STrond Myklebust 
29757bfa891STrond Myklebust 	if (delegation == NULL)
298d25be546STrond Myklebust 		goto out;
299d25be546STrond Myklebust 	spin_lock(&delegation->lock);
3008c75593cSTrond Myklebust 	if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
301be200377STrond Myklebust 		clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags);
3028c75593cSTrond Myklebust 		/* Refcount matched in nfs_end_delegation_return() */
3038c75593cSTrond Myklebust 		ret = nfs_get_delegation(delegation);
3048c75593cSTrond Myklebust 	}
305d25be546STrond Myklebust 	spin_unlock(&delegation->lock);
306efeda80dSTrond Myklebust 	if (ret)
307efeda80dSTrond Myklebust 		nfs_clear_verifier_delegated(&nfsi->vfs_inode);
308d25be546STrond Myklebust out:
309d25be546STrond Myklebust 	return ret;
310d25be546STrond Myklebust }
311d25be546STrond Myklebust 
312d25be546STrond Myklebust static struct nfs_delegation *
nfs_start_delegation_return(struct nfs_inode * nfsi)313d25be546STrond Myklebust nfs_start_delegation_return(struct nfs_inode *nfsi)
314d25be546STrond Myklebust {
315d25be546STrond Myklebust 	struct nfs_delegation *delegation;
316d25be546STrond Myklebust 
317d25be546STrond Myklebust 	rcu_read_lock();
318d25be546STrond Myklebust 	delegation = nfs_start_delegation_return_locked(nfsi);
319d25be546STrond Myklebust 	rcu_read_unlock();
320d25be546STrond Myklebust 	return delegation;
321d25be546STrond Myklebust }
322d25be546STrond Myklebust 
nfs_abort_delegation_return(struct nfs_delegation * delegation,struct nfs_client * clp,int err)323be200377STrond Myklebust static void nfs_abort_delegation_return(struct nfs_delegation *delegation,
324be200377STrond Myklebust 					struct nfs_client *clp, int err)
325d25be546STrond Myklebust {
326dda4b225SChuck Lever 
32734310430STrond Myklebust 	spin_lock(&delegation->lock);
328d25be546STrond Myklebust 	clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
329be200377STrond Myklebust 	if (err == -EAGAIN) {
330be200377STrond Myklebust 		set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags);
331be200377STrond Myklebust 		set_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state);
332be200377STrond Myklebust 	}
333d25be546STrond Myklebust 	spin_unlock(&delegation->lock);
334d25be546STrond Myklebust }
335d25be546STrond Myklebust 
336d25be546STrond Myklebust static struct nfs_delegation *
nfs_detach_delegation_locked(struct nfs_inode * nfsi,struct nfs_delegation * delegation,struct nfs_client * clp)337d25be546STrond Myklebust nfs_detach_delegation_locked(struct nfs_inode *nfsi,
338d25be546STrond Myklebust 		struct nfs_delegation *delegation,
339d25be546STrond Myklebust 		struct nfs_client *clp)
340d25be546STrond Myklebust {
341d25be546STrond Myklebust 	struct nfs_delegation *deleg_cur =
342d25be546STrond Myklebust 		rcu_dereference_protected(nfsi->delegation,
343d25be546STrond Myklebust 				lockdep_is_held(&clp->cl_lock));
344d25be546STrond Myklebust 
345d25be546STrond Myklebust 	if (deleg_cur == NULL || delegation != deleg_cur)
346d25be546STrond Myklebust 		return NULL;
347d25be546STrond Myklebust 
348d25be546STrond Myklebust 	spin_lock(&delegation->lock);
349f9e0cc9cSTrond Myklebust 	if (!delegation->inode) {
350f9e0cc9cSTrond Myklebust 		spin_unlock(&delegation->lock);
351f9e0cc9cSTrond Myklebust 		return NULL;
352f9e0cc9cSTrond Myklebust 	}
35357bfa891STrond Myklebust 	list_del_rcu(&delegation->super_list);
35486e89489STrond Myklebust 	delegation->inode = NULL;
35557bfa891STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, NULL);
35634310430STrond Myklebust 	spin_unlock(&delegation->lock);
35757bfa891STrond Myklebust 	return delegation;
35857bfa891STrond Myklebust }
35957bfa891STrond Myklebust 
nfs_detach_delegation(struct nfs_inode * nfsi,struct nfs_delegation * delegation,struct nfs_server * server)360dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
361d25be546STrond Myklebust 		struct nfs_delegation *delegation,
362d3978bb3SChuck Lever 		struct nfs_server *server)
363dda4b225SChuck Lever {
364d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
365dda4b225SChuck Lever 
366dda4b225SChuck Lever 	spin_lock(&clp->cl_lock);
367d25be546STrond Myklebust 	delegation = nfs_detach_delegation_locked(nfsi, delegation, clp);
368dda4b225SChuck Lever 	spin_unlock(&clp->cl_lock);
369dda4b225SChuck Lever 	return delegation;
370dda4b225SChuck Lever }
371dda4b225SChuck Lever 
372d25be546STrond Myklebust static struct nfs_delegation *
nfs_inode_detach_delegation(struct inode * inode)373d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode)
374d25be546STrond Myklebust {
375d25be546STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
376d25be546STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
377d25be546STrond Myklebust 	struct nfs_delegation *delegation;
378d25be546STrond Myklebust 
379ee05f456STrond Myklebust 	rcu_read_lock();
380ee05f456STrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
381ee05f456STrond Myklebust 	if (delegation != NULL)
382ee05f456STrond Myklebust 		delegation = nfs_detach_delegation(nfsi, delegation, server);
383ee05f456STrond Myklebust 	rcu_read_unlock();
384ee05f456STrond Myklebust 	return delegation;
385d25be546STrond Myklebust }
386d25be546STrond Myklebust 
387cf6726e2STrond Myklebust static void
nfs_update_delegation_cred(struct nfs_delegation * delegation,const struct cred * cred)38857f188e0STrond Myklebust nfs_update_delegation_cred(struct nfs_delegation *delegation,
38957f188e0STrond Myklebust 		const struct cred *cred)
39057f188e0STrond Myklebust {
39157f188e0STrond Myklebust 	const struct cred *old;
39257f188e0STrond Myklebust 
39357f188e0STrond Myklebust 	if (cred_fscmp(delegation->cred, cred) != 0) {
39457f188e0STrond Myklebust 		old = xchg(&delegation->cred, get_cred(cred));
39557f188e0STrond Myklebust 		put_cred(old);
39657f188e0STrond Myklebust 	}
39757f188e0STrond Myklebust }
39857f188e0STrond Myklebust 
39957f188e0STrond Myklebust static void
nfs_update_inplace_delegation(struct nfs_delegation * delegation,const struct nfs_delegation * update)400cf6726e2STrond Myklebust nfs_update_inplace_delegation(struct nfs_delegation *delegation,
401cf6726e2STrond Myklebust 		const struct nfs_delegation *update)
402cf6726e2STrond Myklebust {
403cf6726e2STrond Myklebust 	if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) {
404cf6726e2STrond Myklebust 		delegation->stateid.seqid = update->stateid.seqid;
405cf6726e2STrond Myklebust 		smp_wmb();
406cf6726e2STrond Myklebust 		delegation->type = update->type;
40757f188e0STrond Myklebust 		delegation->pagemod_limit = update->pagemod_limit;
40857f188e0STrond Myklebust 		if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
40957f188e0STrond Myklebust 			delegation->change_attr = update->change_attr;
41057f188e0STrond Myklebust 			nfs_update_delegation_cred(delegation, update->cred);
41157f188e0STrond Myklebust 			/* smp_mb__before_atomic() is implicit due to xchg() */
41257f188e0STrond Myklebust 			clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
413d2269ea1STrond Myklebust 			atomic_long_inc(&nfs_active_delegations);
414cf6726e2STrond Myklebust 		}
415cf6726e2STrond Myklebust 	}
41657f188e0STrond Myklebust }
417cf6726e2STrond Myklebust 
418d3978bb3SChuck Lever /**
419d3978bb3SChuck Lever  * nfs_inode_set_delegation - set up a delegation on an inode
420d3978bb3SChuck Lever  * @inode: inode to which delegation applies
421d3978bb3SChuck Lever  * @cred: cred to use for subsequent delegation processing
42235156bffSTrond Myklebust  * @type: delegation type
42335156bffSTrond Myklebust  * @stateid: delegation stateid
42435156bffSTrond Myklebust  * @pagemod_limit: write delegation "space_limit"
425d3978bb3SChuck Lever  *
426d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
4271da177e4SLinus Torvalds  */
nfs_inode_set_delegation(struct inode * inode,const struct cred * cred,fmode_t type,const nfs4_stateid * stateid,unsigned long pagemod_limit)428a52458b4SNeilBrown int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
42935156bffSTrond Myklebust 				  fmode_t type,
43035156bffSTrond Myklebust 				  const nfs4_stateid *stateid,
43135156bffSTrond Myklebust 				  unsigned long pagemod_limit)
4321da177e4SLinus Torvalds {
433d3978bb3SChuck Lever 	struct nfs_server *server = NFS_SERVER(inode);
434d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
4351da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
43617d2c0a0SDavid Howells 	struct nfs_delegation *delegation, *old_delegation;
43757bfa891STrond Myklebust 	struct nfs_delegation *freeme = NULL;
4381da177e4SLinus Torvalds 	int status = 0;
4391da177e4SLinus Torvalds 
4409c00fd9aSTrond Myklebust 	delegation = kmalloc(sizeof(*delegation), GFP_KERNEL_ACCOUNT);
4411da177e4SLinus Torvalds 	if (delegation == NULL)
4421da177e4SLinus Torvalds 		return -ENOMEM;
44335156bffSTrond Myklebust 	nfs4_stateid_copy(&delegation->stateid, stateid);
4448c75593cSTrond Myklebust 	refcount_set(&delegation->refcount, 1);
44535156bffSTrond Myklebust 	delegation->type = type;
44635156bffSTrond Myklebust 	delegation->pagemod_limit = pagemod_limit;
4471eb5d98fSJeff Layton 	delegation->change_attr = inode_peek_iversion_raw(inode);
448a52458b4SNeilBrown 	delegation->cred = get_cred(cred);
4491da177e4SLinus Torvalds 	delegation->inode = inode;
450b7391f44STrond Myklebust 	delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
45134310430STrond Myklebust 	spin_lock_init(&delegation->lock);
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
45417d2c0a0SDavid Howells 	old_delegation = rcu_dereference_protected(nfsi->delegation,
45517d2c0a0SDavid Howells 					lockdep_is_held(&clp->cl_lock));
456ee05f456STrond Myklebust 	if (old_delegation == NULL)
457ee05f456STrond Myklebust 		goto add_new;
458cf6726e2STrond Myklebust 	/* Is this an update of the existing delegation? */
459cf6726e2STrond Myklebust 	if (nfs4_stateid_match_other(&old_delegation->stateid,
460cf6726e2STrond Myklebust 				&delegation->stateid)) {
461e0f07896STrond Myklebust 		spin_lock(&old_delegation->lock);
462cf6726e2STrond Myklebust 		nfs_update_inplace_delegation(old_delegation,
463cf6726e2STrond Myklebust 				delegation);
464e0f07896STrond Myklebust 		spin_unlock(&old_delegation->lock);
46557bfa891STrond Myklebust 		goto out;
46657bfa891STrond Myklebust 	}
467ee05f456STrond Myklebust 	if (!test_bit(NFS_DELEGATION_REVOKED, &old_delegation->flags)) {
46857bfa891STrond Myklebust 		/*
46957bfa891STrond Myklebust 		 * Deal with broken servers that hand out two
47057bfa891STrond Myklebust 		 * delegations for the same file.
47117280175STrond Myklebust 		 * Allow for upgrades to a WRITE delegation, but
47217280175STrond Myklebust 		 * nothing else.
47357bfa891STrond Myklebust 		 */
47457bfa891STrond Myklebust 		dfprintk(FILE, "%s: server %s handed out "
47557bfa891STrond Myklebust 				"a duplicate delegation!\n",
4763110ff80SHarvey Harrison 				__func__, clp->cl_hostname);
47717280175STrond Myklebust 		if (delegation->type == old_delegation->type ||
47817280175STrond Myklebust 		    !(delegation->type & FMODE_WRITE)) {
47957bfa891STrond Myklebust 			freeme = delegation;
48057bfa891STrond Myklebust 			delegation = NULL;
48157bfa891STrond Myklebust 			goto out;
48257bfa891STrond Myklebust 		}
483ade04647STrond Myklebust 		if (test_and_set_bit(NFS_DELEGATION_RETURNING,
484ade04647STrond Myklebust 					&old_delegation->flags))
485ade04647STrond Myklebust 			goto out;
486ee05f456STrond Myklebust 	}
487ee05f456STrond Myklebust 	freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp);
488d25be546STrond Myklebust 	if (freeme == NULL)
489d25be546STrond Myklebust 		goto out;
490ee05f456STrond Myklebust add_new:
491cc7f2daeSTrond Myklebust 	/*
492cc7f2daeSTrond Myklebust 	 * If we didn't revalidate the change attribute before setting
493cc7f2daeSTrond Myklebust 	 * the delegation, then pre-emptively ask for a full attribute
494cc7f2daeSTrond Myklebust 	 * cache revalidation.
495cc7f2daeSTrond Myklebust 	 */
496cc7f2daeSTrond Myklebust 	spin_lock(&inode->i_lock);
497cc7f2daeSTrond Myklebust 	if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_CHANGE)
498cc7f2daeSTrond Myklebust 		nfs_set_cache_invalid(inode,
499cc7f2daeSTrond Myklebust 			NFS_INO_INVALID_ATIME | NFS_INO_INVALID_CTIME |
500cc7f2daeSTrond Myklebust 			NFS_INO_INVALID_MTIME | NFS_INO_INVALID_SIZE |
501cc7f2daeSTrond Myklebust 			NFS_INO_INVALID_BLOCKS | NFS_INO_INVALID_NLINK |
502cc7f2daeSTrond Myklebust 			NFS_INO_INVALID_OTHER | NFS_INO_INVALID_DATA |
503cc7f2daeSTrond Myklebust 			NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL |
504cc7f2daeSTrond Myklebust 			NFS_INO_INVALID_XATTR);
505cc7f2daeSTrond Myklebust 	spin_unlock(&inode->i_lock);
506cc7f2daeSTrond Myklebust 
50738942ba2STrond Myklebust 	list_add_tail_rcu(&delegation->super_list, &server->delegations);
5088383e460STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, delegation);
5091da177e4SLinus Torvalds 	delegation = NULL;
510412c77ceSTrond Myklebust 
511d2269ea1STrond Myklebust 	atomic_long_inc(&nfs_active_delegations);
512d2269ea1STrond Myklebust 
51335156bffSTrond Myklebust 	trace_nfs4_set_delegation(inode, type);
51457bfa891STrond Myklebust out:
5151da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
516603c83daSTrond Myklebust 	if (delegation != NULL)
517d2269ea1STrond Myklebust 		__nfs_free_delegation(delegation);
518ee05f456STrond Myklebust 	if (freeme != NULL) {
51957bfa891STrond Myklebust 		nfs_do_return_delegation(inode, freeme, 0);
520ee05f456STrond Myklebust 		nfs_free_delegation(freeme);
521ee05f456STrond Myklebust 	}
5221da177e4SLinus Torvalds 	return status;
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds /*
5261da177e4SLinus Torvalds  * Basic procedure for returning a delegation to the server
5271da177e4SLinus Torvalds  */
nfs_end_delegation_return(struct inode * inode,struct nfs_delegation * delegation,int issync)528d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
5291da177e4SLinus Torvalds {
530d25be546STrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
5316b4befc0STrond Myklebust 	unsigned int mode = O_WRONLY | O_RDWR;
532869f9dfaSTrond Myklebust 	int err = 0;
5331da177e4SLinus Torvalds 
534d25be546STrond Myklebust 	if (delegation == NULL)
535d25be546STrond Myklebust 		return 0;
5366b4befc0STrond Myklebust 
5376b4befc0STrond Myklebust 	if (!issync)
5386b4befc0STrond Myklebust 		mode |= O_NONBLOCK;
5396b4befc0STrond Myklebust 	/* Recall of any remaining application leases */
5406b4befc0STrond Myklebust 	err = break_lease(inode, mode);
5416b4befc0STrond Myklebust 
5426b4befc0STrond Myklebust 	while (err == 0) {
543869f9dfaSTrond Myklebust 		if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
544869f9dfaSTrond Myklebust 			break;
54524311f88STrond Myklebust 		err = nfs_delegation_claim_opens(inode, &delegation->stateid,
54624311f88STrond Myklebust 				delegation->type);
547d25be546STrond Myklebust 		if (!issync || err != -EAGAIN)
548d25be546STrond Myklebust 			break;
549d25be546STrond Myklebust 		/*
550d25be546STrond Myklebust 		 * Guard against state recovery
551d25be546STrond Myklebust 		 */
552d25be546STrond Myklebust 		err = nfs4_wait_clnt_recover(clp);
5536b4befc0STrond Myklebust 	}
554d25be546STrond Myklebust 
555d25be546STrond Myklebust 	if (err) {
556be200377STrond Myklebust 		nfs_abort_delegation_return(delegation, clp, err);
557d25be546STrond Myklebust 		goto out;
558d25be546STrond Myklebust 	}
5591da177e4SLinus Torvalds 
560d18cc1fdSTrond Myklebust 	err = nfs_do_return_delegation(inode, delegation, issync);
561d18cc1fdSTrond Myklebust out:
5628c75593cSTrond Myklebust 	/* Refcount matched in nfs_start_delegation_return_locked() */
5638c75593cSTrond Myklebust 	nfs_put_delegation(delegation);
564d18cc1fdSTrond Myklebust 	return err;
56590163027STrond Myklebust }
56690163027STrond Myklebust 
nfs_delegation_need_return(struct nfs_delegation * delegation)567b757144fSTrond Myklebust static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
568b757144fSTrond Myklebust {
569b757144fSTrond Myklebust 	bool ret = false;
570b757144fSTrond Myklebust 
571b757144fSTrond Myklebust 	if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
572b757144fSTrond Myklebust 		ret = true;
5730d104167STrond Myklebust 	else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) {
574b757144fSTrond Myklebust 		struct inode *inode;
575b757144fSTrond Myklebust 
576b757144fSTrond Myklebust 		spin_lock(&delegation->lock);
577b757144fSTrond Myklebust 		inode = delegation->inode;
578b757144fSTrond Myklebust 		if (inode && list_empty(&NFS_I(inode)->open_files))
579b757144fSTrond Myklebust 			ret = true;
580b757144fSTrond Myklebust 		spin_unlock(&delegation->lock);
581b757144fSTrond Myklebust 	}
5820d104167STrond Myklebust 	if (ret)
5830d104167STrond Myklebust 		clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
584af20b7b8STrond Myklebust 	if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) ||
585be200377STrond Myklebust 	    test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) ||
586af20b7b8STrond Myklebust 	    test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
587af20b7b8STrond Myklebust 		ret = false;
588af20b7b8STrond Myklebust 
589b757144fSTrond Myklebust 	return ret;
590b757144fSTrond Myklebust }
591b757144fSTrond Myklebust 
nfs_server_return_marked_delegations(struct nfs_server * server,void __always_unused * data)592af3b61bfSTrond Myklebust static int nfs_server_return_marked_delegations(struct nfs_server *server,
593af3b61bfSTrond Myklebust 		void __always_unused *data)
594af3b61bfSTrond Myklebust {
595af3b61bfSTrond Myklebust 	struct nfs_delegation *delegation;
596af3b61bfSTrond Myklebust 	struct nfs_delegation *prev;
597af3b61bfSTrond Myklebust 	struct inode *inode;
598af3b61bfSTrond Myklebust 	struct inode *place_holder = NULL;
599af3b61bfSTrond Myklebust 	struct nfs_delegation *place_holder_deleg = NULL;
600af3b61bfSTrond Myklebust 	int err = 0;
601af3b61bfSTrond Myklebust 
602af3b61bfSTrond Myklebust restart:
603af3b61bfSTrond Myklebust 	/*
604af3b61bfSTrond Myklebust 	 * To avoid quadratic looping we hold a reference
605af3b61bfSTrond Myklebust 	 * to an inode place_holder.  Each time we restart, we
606af3b61bfSTrond Myklebust 	 * list delegation in the server from the delegations
607af3b61bfSTrond Myklebust 	 * of that inode.
608af3b61bfSTrond Myklebust 	 * prev is an RCU-protected pointer to a delegation which
609af3b61bfSTrond Myklebust 	 * wasn't marked for return and might be a good choice for
610af3b61bfSTrond Myklebust 	 * the next place_holder.
611af3b61bfSTrond Myklebust 	 */
612af3b61bfSTrond Myklebust 	prev = NULL;
613af3b61bfSTrond Myklebust 	delegation = NULL;
614af3b61bfSTrond Myklebust 	rcu_read_lock();
615af3b61bfSTrond Myklebust 	if (place_holder)
616af3b61bfSTrond Myklebust 		delegation = rcu_dereference(NFS_I(place_holder)->delegation);
617af3b61bfSTrond Myklebust 	if (!delegation || delegation != place_holder_deleg)
618af3b61bfSTrond Myklebust 		delegation = list_entry_rcu(server->delegations.next,
619af3b61bfSTrond Myklebust 					    struct nfs_delegation, super_list);
620af3b61bfSTrond Myklebust 	list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) {
621af3b61bfSTrond Myklebust 		struct inode *to_put = NULL;
622af3b61bfSTrond Myklebust 
623af3b61bfSTrond Myklebust 		if (test_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags))
624af3b61bfSTrond Myklebust 			continue;
625af3b61bfSTrond Myklebust 		if (!nfs_delegation_need_return(delegation)) {
626af3b61bfSTrond Myklebust 			if (nfs4_is_valid_delegation(delegation, 0))
627af3b61bfSTrond Myklebust 				prev = delegation;
628af3b61bfSTrond Myklebust 			continue;
629af3b61bfSTrond Myklebust 		}
630af3b61bfSTrond Myklebust 
631af3b61bfSTrond Myklebust 		if (prev) {
632af3b61bfSTrond Myklebust 			struct inode *tmp = nfs_delegation_grab_inode(prev);
633af3b61bfSTrond Myklebust 			if (tmp) {
634af3b61bfSTrond Myklebust 				to_put = place_holder;
635af3b61bfSTrond Myklebust 				place_holder = tmp;
636af3b61bfSTrond Myklebust 				place_holder_deleg = prev;
637af3b61bfSTrond Myklebust 			}
638af3b61bfSTrond Myklebust 		}
639af3b61bfSTrond Myklebust 
640af3b61bfSTrond Myklebust 		inode = nfs_delegation_grab_inode(delegation);
641af3b61bfSTrond Myklebust 		if (inode == NULL) {
642af3b61bfSTrond Myklebust 			rcu_read_unlock();
643af3b61bfSTrond Myklebust 			iput(to_put);
644af3b61bfSTrond Myklebust 			goto restart;
645af3b61bfSTrond Myklebust 		}
646af3b61bfSTrond Myklebust 		delegation = nfs_start_delegation_return_locked(NFS_I(inode));
647af3b61bfSTrond Myklebust 		rcu_read_unlock();
648af3b61bfSTrond Myklebust 
649af3b61bfSTrond Myklebust 		iput(to_put);
650af3b61bfSTrond Myklebust 
651af3b61bfSTrond Myklebust 		err = nfs_end_delegation_return(inode, delegation, 0);
652af3b61bfSTrond Myklebust 		iput(inode);
653af3b61bfSTrond Myklebust 		cond_resched();
654af3b61bfSTrond Myklebust 		if (!err)
655af3b61bfSTrond Myklebust 			goto restart;
656af3b61bfSTrond Myklebust 		set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
657af3b61bfSTrond Myklebust 		goto out;
658af3b61bfSTrond Myklebust 	}
659af3b61bfSTrond Myklebust 	rcu_read_unlock();
660af3b61bfSTrond Myklebust out:
661af3b61bfSTrond Myklebust 	iput(place_holder);
662af3b61bfSTrond Myklebust 	return err;
663af3b61bfSTrond Myklebust }
664af3b61bfSTrond Myklebust 
nfs_server_clear_delayed_delegations(struct nfs_server * server)665be200377STrond Myklebust static bool nfs_server_clear_delayed_delegations(struct nfs_server *server)
666be200377STrond Myklebust {
667be200377STrond Myklebust 	struct nfs_delegation *d;
668be200377STrond Myklebust 	bool ret = false;
669be200377STrond Myklebust 
670be200377STrond Myklebust 	list_for_each_entry_rcu (d, &server->delegations, super_list) {
671be200377STrond Myklebust 		if (!test_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags))
672be200377STrond Myklebust 			continue;
673be200377STrond Myklebust 		nfs_mark_return_delegation(server, d);
674be200377STrond Myklebust 		clear_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags);
675be200377STrond Myklebust 		ret = true;
676be200377STrond Myklebust 	}
677be200377STrond Myklebust 	return ret;
678be200377STrond Myklebust }
679be200377STrond Myklebust 
nfs_client_clear_delayed_delegations(struct nfs_client * clp)680be200377STrond Myklebust static bool nfs_client_clear_delayed_delegations(struct nfs_client *clp)
681be200377STrond Myklebust {
682be200377STrond Myklebust 	struct nfs_server *server;
683be200377STrond Myklebust 	bool ret = false;
684be200377STrond Myklebust 
685be200377STrond Myklebust 	if (!test_and_clear_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state))
686be200377STrond Myklebust 		goto out;
687be200377STrond Myklebust 	rcu_read_lock();
688be200377STrond Myklebust 	list_for_each_entry_rcu (server, &clp->cl_superblocks, client_link) {
689be200377STrond Myklebust 		if (nfs_server_clear_delayed_delegations(server))
690be200377STrond Myklebust 			ret = true;
691be200377STrond Myklebust 	}
692be200377STrond Myklebust 	rcu_read_unlock();
693be200377STrond Myklebust out:
694be200377STrond Myklebust 	return ret;
695be200377STrond Myklebust }
696be200377STrond Myklebust 
697d3978bb3SChuck Lever /**
698d3978bb3SChuck Lever  * nfs_client_return_marked_delegations - return previously marked delegations
699d3978bb3SChuck Lever  * @clp: nfs_client to process
700d3978bb3SChuck Lever  *
701dc327ed4STrond Myklebust  * Note that this function is designed to be called by the state
702dc327ed4STrond Myklebust  * manager thread. For this reason, it cannot flush the dirty data,
703dc327ed4STrond Myklebust  * since that could deadlock in case of a state recovery error.
704dc327ed4STrond Myklebust  *
705d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
706515d8611STrond Myklebust  */
nfs_client_return_marked_delegations(struct nfs_client * clp)707d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp)
708515d8611STrond Myklebust {
709be200377STrond Myklebust 	int err = nfs_client_for_each_server(
710be200377STrond Myklebust 		clp, nfs_server_return_marked_delegations, NULL);
711be200377STrond Myklebust 	if (err)
712be200377STrond Myklebust 		return err;
713be200377STrond Myklebust 	/* If a return was delayed, sleep to prevent hard looping */
714be200377STrond Myklebust 	if (nfs_client_clear_delayed_delegations(clp))
715be200377STrond Myklebust 		ssleep(1);
716be200377STrond Myklebust 	return 0;
717515d8611STrond Myklebust }
718515d8611STrond Myklebust 
719d3978bb3SChuck Lever /**
720b47e0e47STrond Myklebust  * nfs_inode_evict_delegation - return delegation, don't reclaim opens
721d3978bb3SChuck Lever  * @inode: inode to process
722d3978bb3SChuck Lever  *
723d3978bb3SChuck Lever  * Does not protect against delegation reclaims, therefore really only safe
724b47e0e47STrond Myklebust  * to be called from nfs4_clear_inode(). Guaranteed to always free
725b47e0e47STrond Myklebust  * the delegation structure.
726e6f81075STrond Myklebust  */
nfs_inode_evict_delegation(struct inode * inode)727b47e0e47STrond Myklebust void nfs_inode_evict_delegation(struct inode *inode)
728e6f81075STrond Myklebust {
729e6f81075STrond Myklebust 	struct nfs_delegation *delegation;
730e6f81075STrond Myklebust 
731d25be546STrond Myklebust 	delegation = nfs_inode_detach_delegation(inode);
732b47e0e47STrond Myklebust 	if (delegation != NULL) {
733f885ea64STrond Myklebust 		set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
734b47e0e47STrond Myklebust 		set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
7355fcdfaccSTrond Myklebust 		nfs_do_return_delegation(inode, delegation, 1);
736ee05f456STrond Myklebust 		nfs_free_delegation(delegation);
737e6f81075STrond Myklebust 	}
738b47e0e47STrond Myklebust }
739e6f81075STrond Myklebust 
740d3978bb3SChuck Lever /**
7416453bcd0STrond Myklebust  * nfs4_inode_return_delegation - synchronously return a delegation
742d3978bb3SChuck Lever  * @inode: inode to process
743d3978bb3SChuck Lever  *
744c57d1bc5STrond Myklebust  * This routine will always flush any dirty data to disk on the
745c57d1bc5STrond Myklebust  * assumption that if we need to return the delegation, then
746c57d1bc5STrond Myklebust  * we should stop caching.
747c57d1bc5STrond Myklebust  *
748d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
749d3978bb3SChuck Lever  */
nfs4_inode_return_delegation(struct inode * inode)75057ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode)
75190163027STrond Myklebust {
75290163027STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
75390163027STrond Myklebust 	struct nfs_delegation *delegation;
75490163027STrond Myklebust 
755d25be546STrond Myklebust 	delegation = nfs_start_delegation_return(nfsi);
7566e176d47STrond Myklebust 	if (delegation != NULL) {
7576b4befc0STrond Myklebust 		/* Synchronous recall of any application leases */
7586b4befc0STrond Myklebust 		break_lease(inode, O_WRONLY | O_RDWR);
7596e176d47STrond Myklebust 		if (S_ISREG(inode->i_mode))
7606b4befc0STrond Myklebust 			nfs_wb_all(inode);
7616b4befc0STrond Myklebust 		return nfs_end_delegation_return(inode, delegation, 1);
7626e176d47STrond Myklebust 	}
7636b4befc0STrond Myklebust 	return 0;
7641da177e4SLinus Torvalds }
7651da177e4SLinus Torvalds 
766c01d3645STrond Myklebust /**
7676453bcd0STrond Myklebust  * nfs4_inode_return_delegation_on_close - asynchronously return a delegation
768b7b7dac6STrond Myklebust  * @inode: inode to process
769b7b7dac6STrond Myklebust  *
770b7b7dac6STrond Myklebust  * This routine is called on file close in order to determine if the
771b7b7dac6STrond Myklebust  * inode delegation needs to be returned immediately.
772b7b7dac6STrond Myklebust  */
nfs4_inode_return_delegation_on_close(struct inode * inode)773b7b7dac6STrond Myklebust void nfs4_inode_return_delegation_on_close(struct inode *inode)
774b7b7dac6STrond Myklebust {
775b7b7dac6STrond Myklebust 	struct nfs_delegation *delegation;
776b7b7dac6STrond Myklebust 	struct nfs_delegation *ret = NULL;
777b7b7dac6STrond Myklebust 
778b7b7dac6STrond Myklebust 	if (!inode)
779b7b7dac6STrond Myklebust 		return;
780b7b7dac6STrond Myklebust 	rcu_read_lock();
781b7b7dac6STrond Myklebust 	delegation = nfs4_get_valid_delegation(inode);
782b7b7dac6STrond Myklebust 	if (!delegation)
783b7b7dac6STrond Myklebust 		goto out;
78410717f45STrond Myklebust 	if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) ||
78510717f45STrond Myklebust 	    atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) {
786b7b7dac6STrond Myklebust 		spin_lock(&delegation->lock);
787b7b7dac6STrond Myklebust 		if (delegation->inode &&
788b7b7dac6STrond Myklebust 		    list_empty(&NFS_I(inode)->open_files) &&
789b7b7dac6STrond Myklebust 		    !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
790b7b7dac6STrond Myklebust 			clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
7918c75593cSTrond Myklebust 			/* Refcount matched in nfs_end_delegation_return() */
7928c75593cSTrond Myklebust 			ret = nfs_get_delegation(delegation);
793b7b7dac6STrond Myklebust 		}
794b7b7dac6STrond Myklebust 		spin_unlock(&delegation->lock);
795efeda80dSTrond Myklebust 		if (ret)
796efeda80dSTrond Myklebust 			nfs_clear_verifier_delegated(inode);
797b7b7dac6STrond Myklebust 	}
798b7b7dac6STrond Myklebust out:
799b7b7dac6STrond Myklebust 	rcu_read_unlock();
800b7b7dac6STrond Myklebust 	nfs_end_delegation_return(inode, ret, 0);
801b7b7dac6STrond Myklebust }
802b7b7dac6STrond Myklebust 
803b7b7dac6STrond Myklebust /**
804c01d3645STrond Myklebust  * nfs4_inode_make_writeable
805c01d3645STrond Myklebust  * @inode: pointer to inode
806c01d3645STrond Myklebust  *
807c01d3645STrond Myklebust  * Make the inode writeable by returning the delegation if necessary
808c01d3645STrond Myklebust  *
809c01d3645STrond Myklebust  * Returns zero on success, or a negative errno value.
810c01d3645STrond Myklebust  */
nfs4_inode_make_writeable(struct inode * inode)811c01d3645STrond Myklebust int nfs4_inode_make_writeable(struct inode *inode)
812c01d3645STrond Myklebust {
8133887ce1aSTrond Myklebust 	struct nfs_delegation *delegation;
8143887ce1aSTrond Myklebust 
8153887ce1aSTrond Myklebust 	rcu_read_lock();
8163887ce1aSTrond Myklebust 	delegation = nfs4_get_valid_delegation(inode);
8173887ce1aSTrond Myklebust 	if (delegation == NULL ||
8183887ce1aSTrond Myklebust 	    (nfs4_has_session(NFS_SERVER(inode)->nfs_client) &&
8193887ce1aSTrond Myklebust 	     (delegation->type & FMODE_WRITE))) {
8203887ce1aSTrond Myklebust 		rcu_read_unlock();
821c01d3645STrond Myklebust 		return 0;
822c01d3645STrond Myklebust 	}
8233887ce1aSTrond Myklebust 	rcu_read_unlock();
8243887ce1aSTrond Myklebust 	return nfs4_inode_return_delegation(inode);
8253887ce1aSTrond Myklebust }
826c01d3645STrond Myklebust 
nfs_mark_return_if_closed_delegation(struct nfs_server * server,struct nfs_delegation * delegation)827b757144fSTrond Myklebust static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
828b757144fSTrond Myklebust 		struct nfs_delegation *delegation)
829b757144fSTrond Myklebust {
830b757144fSTrond Myklebust 	set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
831b757144fSTrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
832b757144fSTrond Myklebust }
833b757144fSTrond Myklebust 
nfs_server_mark_return_all_delegations(struct nfs_server * server)8345c31e236STrond Myklebust static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
8355c31e236STrond Myklebust {
8365c31e236STrond Myklebust 	struct nfs_delegation *delegation;
8375c31e236STrond Myklebust 	bool ret = false;
8385c31e236STrond Myklebust 
8395c31e236STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
8405c31e236STrond Myklebust 		nfs_mark_return_delegation(server, delegation);
8415c31e236STrond Myklebust 		ret = true;
8425c31e236STrond Myklebust 	}
8435c31e236STrond Myklebust 	return ret;
8445c31e236STrond Myklebust }
8455c31e236STrond Myklebust 
nfs_client_mark_return_all_delegations(struct nfs_client * clp)846b02ba0b6STrond Myklebust static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
847b02ba0b6STrond Myklebust {
848b02ba0b6STrond Myklebust 	struct nfs_server *server;
849b02ba0b6STrond Myklebust 
850b02ba0b6STrond Myklebust 	rcu_read_lock();
851b02ba0b6STrond Myklebust 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
852b02ba0b6STrond Myklebust 		nfs_server_mark_return_all_delegations(server);
853b02ba0b6STrond Myklebust 	rcu_read_unlock();
854b02ba0b6STrond Myklebust }
855b02ba0b6STrond Myklebust 
nfs_delegation_run_state_manager(struct nfs_client * clp)856b02ba0b6STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp)
857b02ba0b6STrond Myklebust {
858b02ba0b6STrond Myklebust 	if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
859b02ba0b6STrond Myklebust 		nfs4_schedule_state_manager(clp);
860b02ba0b6STrond Myklebust }
861b02ba0b6STrond Myklebust 
862b02ba0b6STrond Myklebust /**
863b02ba0b6STrond Myklebust  * nfs_expire_all_delegations
864b02ba0b6STrond Myklebust  * @clp: client to process
865b02ba0b6STrond Myklebust  *
866b02ba0b6STrond Myklebust  */
nfs_expire_all_delegations(struct nfs_client * clp)867b02ba0b6STrond Myklebust void nfs_expire_all_delegations(struct nfs_client *clp)
868b02ba0b6STrond Myklebust {
869b02ba0b6STrond Myklebust 	nfs_client_mark_return_all_delegations(clp);
870b02ba0b6STrond Myklebust 	nfs_delegation_run_state_manager(clp);
871b02ba0b6STrond Myklebust }
872b02ba0b6STrond Myklebust 
873d3978bb3SChuck Lever /**
8746453bcd0STrond Myklebust  * nfs_server_return_all_delegations - return delegations for one superblock
875302fad7bSTrond Myklebust  * @server: pointer to nfs_server to process
876d3978bb3SChuck Lever  *
8771da177e4SLinus Torvalds  */
nfs_server_return_all_delegations(struct nfs_server * server)878eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server)
8791da177e4SLinus Torvalds {
880d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
8815c31e236STrond Myklebust 	bool need_wait;
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds 	if (clp == NULL)
8841da177e4SLinus Torvalds 		return;
885d3978bb3SChuck Lever 
8868383e460STrond Myklebust 	rcu_read_lock();
8875c31e236STrond Myklebust 	need_wait = nfs_server_mark_return_all_delegations(server);
8888383e460STrond Myklebust 	rcu_read_unlock();
889d3978bb3SChuck Lever 
8905c31e236STrond Myklebust 	if (need_wait) {
891d18cc1fdSTrond Myklebust 		nfs4_schedule_state_manager(clp);
8925c31e236STrond Myklebust 		nfs4_wait_clnt_recover(clp);
8935c31e236STrond Myklebust 	}
894515d8611STrond Myklebust }
895515d8611STrond Myklebust 
nfs_mark_return_unused_delegation_types(struct nfs_server * server,fmode_t flags)896826e0013STrond Myklebust static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,
897d3978bb3SChuck Lever 						 fmode_t flags)
898515d8611STrond Myklebust {
899515d8611STrond Myklebust 	struct nfs_delegation *delegation;
900515d8611STrond Myklebust 
901d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
902c79571a5SAlexandros Batsakis 		if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
903c79571a5SAlexandros Batsakis 			continue;
904c79571a5SAlexandros Batsakis 		if (delegation->type & flags)
905826e0013STrond Myklebust 			nfs_mark_return_if_closed_delegation(server, delegation);
906707fb4b3STrond Myklebust 	}
907d3978bb3SChuck Lever }
908d3978bb3SChuck Lever 
nfs_client_mark_return_unused_delegation_types(struct nfs_client * clp,fmode_t flags)909826e0013STrond Myklebust static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,
910d3978bb3SChuck Lever 							fmode_t flags)
911d3978bb3SChuck Lever {
912d3978bb3SChuck Lever 	struct nfs_server *server;
913d3978bb3SChuck Lever 
914d3978bb3SChuck Lever 	rcu_read_lock();
915d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
916826e0013STrond Myklebust 		nfs_mark_return_unused_delegation_types(server, flags);
917515d8611STrond Myklebust 	rcu_read_unlock();
9181da177e4SLinus Torvalds }
9191da177e4SLinus Torvalds 
nfs_revoke_delegation(struct inode * inode,const nfs4_stateid * stateid)920ee05f456STrond Myklebust static void nfs_revoke_delegation(struct inode *inode,
92141020b67STrond Myklebust 		const nfs4_stateid *stateid)
922869f9dfaSTrond Myklebust {
923869f9dfaSTrond Myklebust 	struct nfs_delegation *delegation;
9247f048831STrond Myklebust 	nfs4_stateid tmp;
92541020b67STrond Myklebust 	bool ret = false;
92641020b67STrond Myklebust 
927869f9dfaSTrond Myklebust 	rcu_read_lock();
928869f9dfaSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
92941020b67STrond Myklebust 	if (delegation == NULL)
93041020b67STrond Myklebust 		goto out;
9317f048831STrond Myklebust 	if (stateid == NULL) {
9327f048831STrond Myklebust 		nfs4_stateid_copy(&tmp, &delegation->stateid);
9337f048831STrond Myklebust 		stateid = &tmp;
934f2d47b55STrond Myklebust 	} else {
935f2d47b55STrond Myklebust 		if (!nfs4_stateid_match_other(stateid, &delegation->stateid))
93641020b67STrond Myklebust 			goto out;
937f2d47b55STrond Myklebust 		spin_lock(&delegation->lock);
938f2d47b55STrond Myklebust 		if (stateid->seqid) {
939f2d47b55STrond Myklebust 			if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) {
940f2d47b55STrond Myklebust 				spin_unlock(&delegation->lock);
941f2d47b55STrond Myklebust 				goto out;
942f2d47b55STrond Myklebust 			}
943f2d47b55STrond Myklebust 			delegation->stateid.seqid = stateid->seqid;
944f2d47b55STrond Myklebust 		}
945f2d47b55STrond Myklebust 		spin_unlock(&delegation->lock);
946f2d47b55STrond Myklebust 	}
947d2269ea1STrond Myklebust 	nfs_mark_delegation_revoked(delegation);
94841020b67STrond Myklebust 	ret = true;
94941020b67STrond Myklebust out:
950869f9dfaSTrond Myklebust 	rcu_read_unlock();
9517f048831STrond Myklebust 	if (ret)
9527f048831STrond Myklebust 		nfs_inode_find_state_and_recover(inode, stateid);
953869f9dfaSTrond Myklebust }
954869f9dfaSTrond Myklebust 
nfs_remove_bad_delegation(struct inode * inode,const nfs4_stateid * stateid)95541020b67STrond Myklebust void nfs_remove_bad_delegation(struct inode *inode,
95641020b67STrond Myklebust 		const nfs4_stateid *stateid)
957a1d0b5eeSTrond Myklebust {
958ee05f456STrond Myklebust 	nfs_revoke_delegation(inode, stateid);
959a1d0b5eeSTrond Myklebust }
9609cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
961a1d0b5eeSTrond Myklebust 
nfs_delegation_mark_returned(struct inode * inode,const nfs4_stateid * stateid)962d51f91d2STrond Myklebust void nfs_delegation_mark_returned(struct inode *inode,
963d51f91d2STrond Myklebust 		const nfs4_stateid *stateid)
964d51f91d2STrond Myklebust {
965d51f91d2STrond Myklebust 	struct nfs_delegation *delegation;
966d51f91d2STrond Myklebust 
967d51f91d2STrond Myklebust 	if (!inode)
968d51f91d2STrond Myklebust 		return;
969d51f91d2STrond Myklebust 
970d51f91d2STrond Myklebust 	rcu_read_lock();
971d51f91d2STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
972d51f91d2STrond Myklebust 	if (!delegation)
973d51f91d2STrond Myklebust 		goto out_rcu_unlock;
974d51f91d2STrond Myklebust 
975d51f91d2STrond Myklebust 	spin_lock(&delegation->lock);
976d51f91d2STrond Myklebust 	if (!nfs4_stateid_match_other(stateid, &delegation->stateid))
977d51f91d2STrond Myklebust 		goto out_spin_unlock;
978d51f91d2STrond Myklebust 	if (stateid->seqid) {
979d51f91d2STrond Myklebust 		/* If delegation->stateid is newer, dont mark as returned */
980d51f91d2STrond Myklebust 		if (nfs4_stateid_is_newer(&delegation->stateid, stateid))
981d51f91d2STrond Myklebust 			goto out_clear_returning;
982d51f91d2STrond Myklebust 		if (delegation->stateid.seqid != stateid->seqid)
983d51f91d2STrond Myklebust 			delegation->stateid.seqid = stateid->seqid;
984d51f91d2STrond Myklebust 	}
985d51f91d2STrond Myklebust 
986d2269ea1STrond Myklebust 	nfs_mark_delegation_revoked(delegation);
987d51f91d2STrond Myklebust 
988d51f91d2STrond Myklebust out_clear_returning:
989d51f91d2STrond Myklebust 	clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
990d51f91d2STrond Myklebust out_spin_unlock:
991d51f91d2STrond Myklebust 	spin_unlock(&delegation->lock);
992d51f91d2STrond Myklebust out_rcu_unlock:
993d51f91d2STrond Myklebust 	rcu_read_unlock();
994d51f91d2STrond Myklebust 
995d51f91d2STrond Myklebust 	nfs_inode_find_state_and_recover(inode, stateid);
996d51f91d2STrond Myklebust }
997d51f91d2STrond Myklebust 
998d3978bb3SChuck Lever /**
999826e0013STrond Myklebust  * nfs_expire_unused_delegation_types
1000d3978bb3SChuck Lever  * @clp: client to process
1001d3978bb3SChuck Lever  * @flags: delegation types to expire
1002d3978bb3SChuck Lever  *
1003d3978bb3SChuck Lever  */
nfs_expire_unused_delegation_types(struct nfs_client * clp,fmode_t flags)1004826e0013STrond Myklebust void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)
1005c79571a5SAlexandros Batsakis {
1006826e0013STrond Myklebust 	nfs_client_mark_return_unused_delegation_types(clp, flags);
1007c79571a5SAlexandros Batsakis 	nfs_delegation_run_state_manager(clp);
1008c79571a5SAlexandros Batsakis }
1009c79571a5SAlexandros Batsakis 
nfs_mark_return_unreferenced_delegations(struct nfs_server * server)1010d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
1011b7391f44STrond Myklebust {
1012b7391f44STrond Myklebust 	struct nfs_delegation *delegation;
1013b7391f44STrond Myklebust 
1014d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
1015b7391f44STrond Myklebust 		if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
1016b7391f44STrond Myklebust 			continue;
1017b757144fSTrond Myklebust 		nfs_mark_return_if_closed_delegation(server, delegation);
1018b7391f44STrond Myklebust 	}
1019b7391f44STrond Myklebust }
1020b7391f44STrond Myklebust 
1021d3978bb3SChuck Lever /**
1022d3978bb3SChuck Lever  * nfs_expire_unreferenced_delegations - Eliminate unused delegations
1023d3978bb3SChuck Lever  * @clp: nfs_client to process
1024d3978bb3SChuck Lever  *
1025d3978bb3SChuck Lever  */
nfs_expire_unreferenced_delegations(struct nfs_client * clp)1026b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
1027b7391f44STrond Myklebust {
1028d3978bb3SChuck Lever 	struct nfs_server *server;
1029d3978bb3SChuck Lever 
1030d3978bb3SChuck Lever 	rcu_read_lock();
1031d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
1032d3978bb3SChuck Lever 		nfs_mark_return_unreferenced_delegations(server);
1033d3978bb3SChuck Lever 	rcu_read_unlock();
1034d3978bb3SChuck Lever 
1035b7391f44STrond Myklebust 	nfs_delegation_run_state_manager(clp);
1036b7391f44STrond Myklebust }
1037b7391f44STrond Myklebust 
1038d3978bb3SChuck Lever /**
1039d3978bb3SChuck Lever  * nfs_async_inode_return_delegation - asynchronously return a delegation
1040d3978bb3SChuck Lever  * @inode: inode to process
10418e663f0eSTrond Myklebust  * @stateid: state ID information
1042d3978bb3SChuck Lever  *
1043d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
10441da177e4SLinus Torvalds  */
nfs_async_inode_return_delegation(struct inode * inode,const nfs4_stateid * stateid)1045d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode,
1046d3978bb3SChuck Lever 				      const nfs4_stateid *stateid)
10471da177e4SLinus Torvalds {
1048ed1e6211STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
1049ed1e6211STrond Myklebust 	struct nfs_client *clp = server->nfs_client;
10506411bd4aSTrond Myklebust 	struct nfs_delegation *delegation;
10511da177e4SLinus Torvalds 
10526411bd4aSTrond Myklebust 	rcu_read_lock();
1053457a5042STrond Myklebust 	delegation = nfs4_get_valid_delegation(inode);
1054755a48a7STrond Myklebust 	if (delegation == NULL)
1055755a48a7STrond Myklebust 		goto out_enoent;
10564816fdadSTrond Myklebust 	if (stateid != NULL &&
10574816fdadSTrond Myklebust 	    !clp->cl_mvops->match_stateid(&delegation->stateid, stateid))
1058755a48a7STrond Myklebust 		goto out_enoent;
1059ed1e6211STrond Myklebust 	nfs_mark_return_delegation(server, delegation);
10606411bd4aSTrond Myklebust 	rcu_read_unlock();
1061d3978bb3SChuck Lever 
10626b4befc0STrond Myklebust 	/* If there are any application leases or delegations, recall them */
10636b4befc0STrond Myklebust 	break_lease(inode, O_WRONLY | O_RDWR | O_NONBLOCK);
10646b4befc0STrond Myklebust 
10656411bd4aSTrond Myklebust 	nfs_delegation_run_state_manager(clp);
10666411bd4aSTrond Myklebust 	return 0;
1067755a48a7STrond Myklebust out_enoent:
1068755a48a7STrond Myklebust 	rcu_read_unlock();
1069755a48a7STrond Myklebust 	return -ENOENT;
10701da177e4SLinus Torvalds }
10711da177e4SLinus Torvalds 
1072d3978bb3SChuck Lever static struct inode *
nfs_delegation_find_inode_server(struct nfs_server * server,const struct nfs_fh * fhandle)1073d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server,
1074d3978bb3SChuck Lever 				 const struct nfs_fh *fhandle)
10751da177e4SLinus Torvalds {
10761da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
1077113aac6dSTrond Myklebust 	struct super_block *freeme = NULL;
1078113aac6dSTrond Myklebust 	struct inode *res = NULL;
1079d3978bb3SChuck Lever 
1080d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
108186e89489STrond Myklebust 		spin_lock(&delegation->lock);
108286e89489STrond Myklebust 		if (delegation->inode != NULL &&
1083457a5042STrond Myklebust 		    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
108486e89489STrond Myklebust 		    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
1085113aac6dSTrond Myklebust 			if (nfs_sb_active(server->super)) {
1086113aac6dSTrond Myklebust 				freeme = server->super;
1087113aac6dSTrond Myklebust 				res = igrab(delegation->inode);
1088113aac6dSTrond Myklebust 			}
108986e89489STrond Myklebust 			spin_unlock(&delegation->lock);
109086e89489STrond Myklebust 			if (res != NULL)
1091d3978bb3SChuck Lever 				return res;
1092e39d8a18STrond Myklebust 			if (freeme) {
1093e39d8a18STrond Myklebust 				rcu_read_unlock();
1094113aac6dSTrond Myklebust 				nfs_sb_deactive(freeme);
1095e39d8a18STrond Myklebust 				rcu_read_lock();
1096e39d8a18STrond Myklebust 			}
10976c342655STrond Myklebust 			return ERR_PTR(-EAGAIN);
10986c342655STrond Myklebust 		}
10996c342655STrond Myklebust 		spin_unlock(&delegation->lock);
11006c342655STrond Myklebust 	}
11016c342655STrond Myklebust 	return ERR_PTR(-ENOENT);
1102d3978bb3SChuck Lever }
1103d3978bb3SChuck Lever 
1104d3978bb3SChuck Lever /**
1105d3978bb3SChuck Lever  * nfs_delegation_find_inode - retrieve the inode associated with a delegation
1106d3978bb3SChuck Lever  * @clp: client state handle
1107d3978bb3SChuck Lever  * @fhandle: filehandle from a delegation recall
1108d3978bb3SChuck Lever  *
1109d3978bb3SChuck Lever  * Returns pointer to inode matching "fhandle," or NULL if a matching inode
1110d3978bb3SChuck Lever  * cannot be found.
1111d3978bb3SChuck Lever  */
nfs_delegation_find_inode(struct nfs_client * clp,const struct nfs_fh * fhandle)1112d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
1113d3978bb3SChuck Lever 					const struct nfs_fh *fhandle)
1114d3978bb3SChuck Lever {
1115d3978bb3SChuck Lever 	struct nfs_server *server;
11166c342655STrond Myklebust 	struct inode *res;
1117d3978bb3SChuck Lever 
1118d3978bb3SChuck Lever 	rcu_read_lock();
1119d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
1120d3978bb3SChuck Lever 		res = nfs_delegation_find_inode_server(server, fhandle);
1121d5681f59SAnna Schumaker 		if (res != ERR_PTR(-ENOENT)) {
1122d5681f59SAnna Schumaker 			rcu_read_unlock();
11236c342655STrond Myklebust 			return res;
1124d3978bb3SChuck Lever 		}
1125d5681f59SAnna Schumaker 	}
11268383e460STrond Myklebust 	rcu_read_unlock();
11276c342655STrond Myklebust 	return ERR_PTR(-ENOENT);
11281da177e4SLinus Torvalds }
11291da177e4SLinus Torvalds 
nfs_delegation_mark_reclaim_server(struct nfs_server * server)1130d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
1131d3978bb3SChuck Lever {
1132d3978bb3SChuck Lever 	struct nfs_delegation *delegation;
1133d3978bb3SChuck Lever 
113445870d69STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
113545870d69STrond Myklebust 		/*
113645870d69STrond Myklebust 		 * If the delegation may have been admin revoked, then we
113745870d69STrond Myklebust 		 * cannot reclaim it.
113845870d69STrond Myklebust 		 */
113945870d69STrond Myklebust 		if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags))
114045870d69STrond Myklebust 			continue;
1141d3978bb3SChuck Lever 		set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
1142d3978bb3SChuck Lever 	}
114345870d69STrond Myklebust }
1144d3978bb3SChuck Lever 
1145d3978bb3SChuck Lever /**
1146d3978bb3SChuck Lever  * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed
1147d3978bb3SChuck Lever  * @clp: nfs_client to process
1148d3978bb3SChuck Lever  *
11491da177e4SLinus Torvalds  */
nfs_delegation_mark_reclaim(struct nfs_client * clp)1150adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp)
11511da177e4SLinus Torvalds {
1152d3978bb3SChuck Lever 	struct nfs_server *server;
1153d3978bb3SChuck Lever 
11548383e460STrond Myklebust 	rcu_read_lock();
1155d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
1156d3978bb3SChuck Lever 		nfs_delegation_mark_reclaim_server(server);
11578383e460STrond Myklebust 	rcu_read_unlock();
11581da177e4SLinus Torvalds }
11591da177e4SLinus Torvalds 
nfs_server_reap_unclaimed_delegations(struct nfs_server * server,void __always_unused * data)11601bba38b2STrond Myklebust static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server,
11611bba38b2STrond Myklebust 		void __always_unused *data)
11621da177e4SLinus Torvalds {
11638383e460STrond Myklebust 	struct nfs_delegation *delegation;
116486e89489STrond Myklebust 	struct inode *inode;
11658383e460STrond Myklebust restart:
11668383e460STrond Myklebust 	rcu_read_lock();
11671bba38b2STrond Myklebust restart_locked:
11681bba38b2STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
11696f9449beSTrond Myklebust 		if (test_bit(NFS_DELEGATION_INODE_FREEING,
11706f9449beSTrond Myklebust 					&delegation->flags) ||
11716f9449beSTrond Myklebust 		    test_bit(NFS_DELEGATION_RETURNING,
11726f9449beSTrond Myklebust 					&delegation->flags) ||
11736f9449beSTrond Myklebust 		    test_bit(NFS_DELEGATION_NEED_RECLAIM,
1174d3978bb3SChuck Lever 					&delegation->flags) == 0)
11751da177e4SLinus Torvalds 			continue;
11769f0f8e12STrond Myklebust 		inode = nfs_delegation_grab_inode(delegation);
11771bba38b2STrond Myklebust 		if (inode == NULL)
11781bba38b2STrond Myklebust 			goto restart_locked;
1179b04b22f4STrond Myklebust 		delegation = nfs_start_delegation_return_locked(NFS_I(inode));
1180b04b22f4STrond Myklebust 		rcu_read_unlock();
1181b04b22f4STrond Myklebust 		if (delegation != NULL) {
11828c75593cSTrond Myklebust 			if (nfs_detach_delegation(NFS_I(inode), delegation,
11838c75593cSTrond Myklebust 						server) != NULL)
1184905f8d16STrond Myklebust 				nfs_free_delegation(delegation);
11858c75593cSTrond Myklebust 			/* Match nfs_start_delegation_return_locked */
11868c75593cSTrond Myklebust 			nfs_put_delegation(delegation);
1187b04b22f4STrond Myklebust 		}
118886e89489STrond Myklebust 		iput(inode);
11893ca951b6SNeilBrown 		cond_resched();
11908383e460STrond Myklebust 		goto restart;
11911da177e4SLinus Torvalds 	}
11928383e460STrond Myklebust 	rcu_read_unlock();
11931bba38b2STrond Myklebust 	return 0;
11941bba38b2STrond Myklebust }
11951bba38b2STrond Myklebust 
11961bba38b2STrond Myklebust /**
11971bba38b2STrond Myklebust  * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done
11981bba38b2STrond Myklebust  * @clp: nfs_client to process
11991bba38b2STrond Myklebust  *
12001bba38b2STrond Myklebust  */
nfs_delegation_reap_unclaimed(struct nfs_client * clp)12011bba38b2STrond Myklebust void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
12021bba38b2STrond Myklebust {
12031bba38b2STrond Myklebust 	nfs_client_for_each_server(clp, nfs_server_reap_unclaimed_delegations,
12041bba38b2STrond Myklebust 			NULL);
12051da177e4SLinus Torvalds }
12063e4f6290STrond Myklebust 
nfs4_server_rebooted(const struct nfs_client * clp)1207bb3d1a3bSTrond Myklebust static inline bool nfs4_server_rebooted(const struct nfs_client *clp)
1208bb3d1a3bSTrond Myklebust {
1209bb3d1a3bSTrond Myklebust 	return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) |
1210bb3d1a3bSTrond Myklebust 				BIT(NFS4CLNT_LEASE_EXPIRED) |
1211bb3d1a3bSTrond Myklebust 				BIT(NFS4CLNT_SESSION_RESET))) != 0;
1212bb3d1a3bSTrond Myklebust }
1213bb3d1a3bSTrond Myklebust 
nfs_mark_test_expired_delegation(struct nfs_server * server,struct nfs_delegation * delegation)121445870d69STrond Myklebust static void nfs_mark_test_expired_delegation(struct nfs_server *server,
121545870d69STrond Myklebust 	    struct nfs_delegation *delegation)
121645870d69STrond Myklebust {
1217059b43e9STrond Myklebust 	if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE)
1218059b43e9STrond Myklebust 		return;
121945870d69STrond Myklebust 	clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
122045870d69STrond Myklebust 	set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
122145870d69STrond Myklebust 	set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state);
122245870d69STrond Myklebust }
122345870d69STrond Myklebust 
nfs_inode_mark_test_expired_delegation(struct nfs_server * server,struct inode * inode)1224bb3d1a3bSTrond Myklebust static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server,
1225bb3d1a3bSTrond Myklebust 		struct inode *inode)
1226bb3d1a3bSTrond Myklebust {
1227bb3d1a3bSTrond Myklebust 	struct nfs_delegation *delegation;
1228bb3d1a3bSTrond Myklebust 
1229bb3d1a3bSTrond Myklebust 	rcu_read_lock();
1230bb3d1a3bSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
1231bb3d1a3bSTrond Myklebust 	if (delegation)
1232bb3d1a3bSTrond Myklebust 		nfs_mark_test_expired_delegation(server, delegation);
1233bb3d1a3bSTrond Myklebust 	rcu_read_unlock();
1234bb3d1a3bSTrond Myklebust 
1235bb3d1a3bSTrond Myklebust }
1236bb3d1a3bSTrond Myklebust 
nfs_delegation_mark_test_expired_server(struct nfs_server * server)123745870d69STrond Myklebust static void nfs_delegation_mark_test_expired_server(struct nfs_server *server)
123845870d69STrond Myklebust {
123945870d69STrond Myklebust 	struct nfs_delegation *delegation;
124045870d69STrond Myklebust 
124145870d69STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list)
124245870d69STrond Myklebust 		nfs_mark_test_expired_delegation(server, delegation);
124345870d69STrond Myklebust }
124445870d69STrond Myklebust 
124545870d69STrond Myklebust /**
124645870d69STrond Myklebust  * nfs_mark_test_expired_all_delegations - mark all delegations for testing
124745870d69STrond Myklebust  * @clp: nfs_client to process
124845870d69STrond Myklebust  *
124945870d69STrond Myklebust  * Iterates through all the delegations associated with this server and
125045870d69STrond Myklebust  * marks them as needing to be checked for validity.
125145870d69STrond Myklebust  */
nfs_mark_test_expired_all_delegations(struct nfs_client * clp)125245870d69STrond Myklebust void nfs_mark_test_expired_all_delegations(struct nfs_client *clp)
125345870d69STrond Myklebust {
125445870d69STrond Myklebust 	struct nfs_server *server;
125545870d69STrond Myklebust 
125645870d69STrond Myklebust 	rcu_read_lock();
125745870d69STrond Myklebust 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
125845870d69STrond Myklebust 		nfs_delegation_mark_test_expired_server(server);
125945870d69STrond Myklebust 	rcu_read_unlock();
126045870d69STrond Myklebust }
126145870d69STrond Myklebust 
126245870d69STrond Myklebust /**
12638ca017c8SScott Mayhew  * nfs_test_expired_all_delegations - test all delegations for a client
12648ca017c8SScott Mayhew  * @clp: nfs_client to process
12658ca017c8SScott Mayhew  *
12668ca017c8SScott Mayhew  * Helper for handling "recallable state revoked" status from server.
12678ca017c8SScott Mayhew  */
nfs_test_expired_all_delegations(struct nfs_client * clp)12688ca017c8SScott Mayhew void nfs_test_expired_all_delegations(struct nfs_client *clp)
12698ca017c8SScott Mayhew {
12708ca017c8SScott Mayhew 	nfs_mark_test_expired_all_delegations(clp);
12718ca017c8SScott Mayhew 	nfs4_schedule_state_manager(clp);
12728ca017c8SScott Mayhew }
12738ca017c8SScott Mayhew 
1274ad114089STrond Myklebust static void
nfs_delegation_test_free_expired(struct inode * inode,nfs4_stateid * stateid,const struct cred * cred)1275ad114089STrond Myklebust nfs_delegation_test_free_expired(struct inode *inode,
1276ad114089STrond Myklebust 		nfs4_stateid *stateid,
1277ad114089STrond Myklebust 		const struct cred *cred)
1278ad114089STrond Myklebust {
1279ad114089STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
1280ad114089STrond Myklebust 	const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops;
1281ad114089STrond Myklebust 	int status;
1282ad114089STrond Myklebust 
1283ad114089STrond Myklebust 	if (!cred)
1284ad114089STrond Myklebust 		return;
1285ad114089STrond Myklebust 	status = ops->test_and_free_expired(server, stateid, cred);
1286ad114089STrond Myklebust 	if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID)
1287ad114089STrond Myklebust 		nfs_remove_bad_delegation(inode, stateid);
1288ad114089STrond Myklebust }
1289ad114089STrond Myklebust 
nfs_server_reap_expired_delegations(struct nfs_server * server,void __always_unused * data)12907f156ef0STrond Myklebust static int nfs_server_reap_expired_delegations(struct nfs_server *server,
12917f156ef0STrond Myklebust 		void __always_unused *data)
12927f156ef0STrond Myklebust {
12937f156ef0STrond Myklebust 	struct nfs_delegation *delegation;
12947f156ef0STrond Myklebust 	struct inode *inode;
12957f156ef0STrond Myklebust 	const struct cred *cred;
12967f156ef0STrond Myklebust 	nfs4_stateid stateid;
12977f156ef0STrond Myklebust restart:
12987f156ef0STrond Myklebust 	rcu_read_lock();
12997f156ef0STrond Myklebust restart_locked:
13007f156ef0STrond Myklebust 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
13017f156ef0STrond Myklebust 		if (test_bit(NFS_DELEGATION_INODE_FREEING,
13027f156ef0STrond Myklebust 					&delegation->flags) ||
13037f156ef0STrond Myklebust 		    test_bit(NFS_DELEGATION_RETURNING,
13047f156ef0STrond Myklebust 					&delegation->flags) ||
13057f156ef0STrond Myklebust 		    test_bit(NFS_DELEGATION_TEST_EXPIRED,
13067f156ef0STrond Myklebust 					&delegation->flags) == 0)
13077f156ef0STrond Myklebust 			continue;
13087f156ef0STrond Myklebust 		inode = nfs_delegation_grab_inode(delegation);
13097f156ef0STrond Myklebust 		if (inode == NULL)
13107f156ef0STrond Myklebust 			goto restart_locked;
1311fc51b1cfSTrond Myklebust 		spin_lock(&delegation->lock);
13127f156ef0STrond Myklebust 		cred = get_cred_rcu(delegation->cred);
13137f156ef0STrond Myklebust 		nfs4_stateid_copy(&stateid, &delegation->stateid);
1314fc51b1cfSTrond Myklebust 		spin_unlock(&delegation->lock);
13157f156ef0STrond Myklebust 		clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
13167f156ef0STrond Myklebust 		rcu_read_unlock();
13177f156ef0STrond Myklebust 		nfs_delegation_test_free_expired(inode, &stateid, cred);
13187f156ef0STrond Myklebust 		put_cred(cred);
13197f156ef0STrond Myklebust 		if (!nfs4_server_rebooted(server->nfs_client)) {
13207f156ef0STrond Myklebust 			iput(inode);
13217f156ef0STrond Myklebust 			cond_resched();
13227f156ef0STrond Myklebust 			goto restart;
13237f156ef0STrond Myklebust 		}
13247f156ef0STrond Myklebust 		nfs_inode_mark_test_expired_delegation(server,inode);
13257f156ef0STrond Myklebust 		iput(inode);
13267f156ef0STrond Myklebust 		return -EAGAIN;
13277f156ef0STrond Myklebust 	}
13287f156ef0STrond Myklebust 	rcu_read_unlock();
13297f156ef0STrond Myklebust 	return 0;
13307f156ef0STrond Myklebust }
13317f156ef0STrond Myklebust 
13328ca017c8SScott Mayhew /**
133345870d69STrond Myklebust  * nfs_reap_expired_delegations - reap expired delegations
133445870d69STrond Myklebust  * @clp: nfs_client to process
133545870d69STrond Myklebust  *
133645870d69STrond Myklebust  * Iterates through all the delegations associated with this server and
133745870d69STrond Myklebust  * checks if they have may have been revoked. This function is usually
133845870d69STrond Myklebust  * expected to be called in cases where the server may have lost its
133945870d69STrond Myklebust  * lease.
134045870d69STrond Myklebust  */
nfs_reap_expired_delegations(struct nfs_client * clp)134145870d69STrond Myklebust void nfs_reap_expired_delegations(struct nfs_client *clp)
134245870d69STrond Myklebust {
13437f156ef0STrond Myklebust 	nfs_client_for_each_server(clp, nfs_server_reap_expired_delegations,
13447f156ef0STrond Myklebust 			NULL);
134545870d69STrond Myklebust }
134645870d69STrond Myklebust 
nfs_inode_find_delegation_state_and_recover(struct inode * inode,const nfs4_stateid * stateid)13476c2d8f8dSTrond Myklebust void nfs_inode_find_delegation_state_and_recover(struct inode *inode,
13486c2d8f8dSTrond Myklebust 		const nfs4_stateid *stateid)
13496c2d8f8dSTrond Myklebust {
13506c2d8f8dSTrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
13516c2d8f8dSTrond Myklebust 	struct nfs_delegation *delegation;
13526c2d8f8dSTrond Myklebust 	bool found = false;
13536c2d8f8dSTrond Myklebust 
13546c2d8f8dSTrond Myklebust 	rcu_read_lock();
13556c2d8f8dSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
13566c2d8f8dSTrond Myklebust 	if (delegation &&
135742c304c3STrond Myklebust 	    nfs4_stateid_match_or_older(&delegation->stateid, stateid) &&
135842c304c3STrond Myklebust 	    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
13596c2d8f8dSTrond Myklebust 		nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation);
13606c2d8f8dSTrond Myklebust 		found = true;
13616c2d8f8dSTrond Myklebust 	}
13626c2d8f8dSTrond Myklebust 	rcu_read_unlock();
13636c2d8f8dSTrond Myklebust 	if (found)
13646c2d8f8dSTrond Myklebust 		nfs4_schedule_state_manager(clp);
13656c2d8f8dSTrond Myklebust }
13666c2d8f8dSTrond Myklebust 
1367d3978bb3SChuck Lever /**
1368d3978bb3SChuck Lever  * nfs_delegations_present - check for existence of delegations
1369d3978bb3SChuck Lever  * @clp: client state handle
1370d3978bb3SChuck Lever  *
1371d3978bb3SChuck Lever  * Returns one if there are any nfs_delegation structures attached
1372d3978bb3SChuck Lever  * to this nfs_client.
1373d3978bb3SChuck Lever  */
nfs_delegations_present(struct nfs_client * clp)1374d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp)
1375d3978bb3SChuck Lever {
1376d3978bb3SChuck Lever 	struct nfs_server *server;
1377d3978bb3SChuck Lever 	int ret = 0;
1378d3978bb3SChuck Lever 
1379d3978bb3SChuck Lever 	rcu_read_lock();
1380d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
1381d3978bb3SChuck Lever 		if (!list_empty(&server->delegations)) {
1382d3978bb3SChuck Lever 			ret = 1;
1383d3978bb3SChuck Lever 			break;
1384d3978bb3SChuck Lever 		}
1385d3978bb3SChuck Lever 	rcu_read_unlock();
1386d3978bb3SChuck Lever 	return ret;
1387d3978bb3SChuck Lever }
1388d3978bb3SChuck Lever 
1389d3978bb3SChuck Lever /**
139012f275cdSTrond Myklebust  * nfs4_refresh_delegation_stateid - Update delegation stateid seqid
139112f275cdSTrond Myklebust  * @dst: stateid to refresh
139212f275cdSTrond Myklebust  * @inode: inode to check
139312f275cdSTrond Myklebust  *
139412f275cdSTrond Myklebust  * Returns "true" and updates "dst->seqid" * if inode had a delegation
139512f275cdSTrond Myklebust  * that matches our delegation stateid. Otherwise "false" is returned.
139612f275cdSTrond Myklebust  */
nfs4_refresh_delegation_stateid(nfs4_stateid * dst,struct inode * inode)139712f275cdSTrond Myklebust bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
139812f275cdSTrond Myklebust {
139912f275cdSTrond Myklebust 	struct nfs_delegation *delegation;
140012f275cdSTrond Myklebust 	bool ret = false;
140112f275cdSTrond Myklebust 	if (!inode)
140212f275cdSTrond Myklebust 		goto out;
140312f275cdSTrond Myklebust 
140412f275cdSTrond Myklebust 	rcu_read_lock();
140512f275cdSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
140612f275cdSTrond Myklebust 	if (delegation != NULL &&
1407b5756208STrond Myklebust 	    nfs4_stateid_match_other(dst, &delegation->stateid) &&
1408246afc0aSTrond Myklebust 	    nfs4_stateid_is_newer(&delegation->stateid, dst) &&
1409b5756208STrond Myklebust 	    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
141012f275cdSTrond Myklebust 		dst->seqid = delegation->stateid.seqid;
141179cc5542STrond Myklebust 		ret = true;
141212f275cdSTrond Myklebust 	}
141312f275cdSTrond Myklebust 	rcu_read_unlock();
141412f275cdSTrond Myklebust out:
141512f275cdSTrond Myklebust 	return ret;
141612f275cdSTrond Myklebust }
141712f275cdSTrond Myklebust 
141812f275cdSTrond Myklebust /**
1419d3978bb3SChuck Lever  * nfs4_copy_delegation_stateid - Copy inode's state ID information
1420d3978bb3SChuck Lever  * @inode: inode to check
14210032a7a7STrond Myklebust  * @flags: delegation type requirement
1422abf4e13cSTrond Myklebust  * @dst: stateid data structure to fill in
1423abf4e13cSTrond Myklebust  * @cred: optional argument to retrieve credential
1424d3978bb3SChuck Lever  *
14250032a7a7STrond Myklebust  * Returns "true" and fills in "dst->data" * if inode had a delegation,
14260032a7a7STrond Myklebust  * otherwise "false" is returned.
1427d3978bb3SChuck Lever  */
nfs4_copy_delegation_stateid(struct inode * inode,fmode_t flags,nfs4_stateid * dst,const struct cred ** cred)1428abf4e13cSTrond Myklebust bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags,
1429a52458b4SNeilBrown 		nfs4_stateid *dst, const struct cred **cred)
14303e4f6290STrond Myklebust {
14313e4f6290STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
14323e4f6290STrond Myklebust 	struct nfs_delegation *delegation;
1433fc51b1cfSTrond Myklebust 	bool ret = false;
14343e4f6290STrond Myklebust 
14350032a7a7STrond Myklebust 	flags &= FMODE_READ|FMODE_WRITE;
14368383e460STrond Myklebust 	rcu_read_lock();
14378383e460STrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
1438fc51b1cfSTrond Myklebust 	if (!delegation)
1439fc51b1cfSTrond Myklebust 		goto out;
1440fc51b1cfSTrond Myklebust 	spin_lock(&delegation->lock);
1441aa05c87fSTrond Myklebust 	ret = nfs4_is_valid_delegation(delegation, flags);
14420032a7a7STrond Myklebust 	if (ret) {
1443f597c537STrond Myklebust 		nfs4_stateid_copy(dst, &delegation->stateid);
14440032a7a7STrond Myklebust 		nfs_mark_delegation_referenced(delegation);
1445abf4e13cSTrond Myklebust 		if (cred)
1446a52458b4SNeilBrown 			*cred = get_cred(delegation->cred);
14473e4f6290STrond Myklebust 	}
1448fc51b1cfSTrond Myklebust 	spin_unlock(&delegation->lock);
1449fc51b1cfSTrond Myklebust out:
14508383e460STrond Myklebust 	rcu_read_unlock();
14518383e460STrond Myklebust 	return ret;
14523e4f6290STrond Myklebust }
14535445b1fbSTrond Myklebust 
14545445b1fbSTrond Myklebust /**
14555445b1fbSTrond Myklebust  * nfs4_delegation_flush_on_close - Check if we must flush file on close
14565445b1fbSTrond Myklebust  * @inode: inode to check
14575445b1fbSTrond Myklebust  *
14585445b1fbSTrond Myklebust  * This function checks the number of outstanding writes to the file
14595445b1fbSTrond Myklebust  * against the delegation 'space_limit' field to see if
14605445b1fbSTrond Myklebust  * the spec requires us to flush the file on close.
14615445b1fbSTrond Myklebust  */
nfs4_delegation_flush_on_close(const struct inode * inode)14625445b1fbSTrond Myklebust bool nfs4_delegation_flush_on_close(const struct inode *inode)
14635445b1fbSTrond Myklebust {
14645445b1fbSTrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
14655445b1fbSTrond Myklebust 	struct nfs_delegation *delegation;
14665445b1fbSTrond Myklebust 	bool ret = true;
14675445b1fbSTrond Myklebust 
14685445b1fbSTrond Myklebust 	rcu_read_lock();
14695445b1fbSTrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
14705445b1fbSTrond Myklebust 	if (delegation == NULL || !(delegation->type & FMODE_WRITE))
14715445b1fbSTrond Myklebust 		goto out;
1472a6b6d5b8STrond Myklebust 	if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit)
14735445b1fbSTrond Myklebust 		ret = false;
14745445b1fbSTrond Myklebust out:
14755445b1fbSTrond Myklebust 	rcu_read_unlock();
14765445b1fbSTrond Myklebust 	return ret;
14775445b1fbSTrond Myklebust }
147810717f45STrond Myklebust 
147910717f45STrond Myklebust module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
1480