xref: /openbmc/linux/fs/nfs/delegation.c (revision db4f2e63)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/fs/nfs/delegation.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 2004 Trond Myklebust
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * NFS file delegation management
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds #include <linux/completion.h>
1058d9714aSTrond Myklebust #include <linux/kthread.h>
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/sched.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/spinlock.h>
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/nfs4.h>
171da177e4SLinus Torvalds #include <linux/nfs_fs.h>
181da177e4SLinus Torvalds #include <linux/nfs_xdr.h>
191da177e4SLinus Torvalds 
204ce79717STrond Myklebust #include "nfs4_fs.h"
211da177e4SLinus Torvalds #include "delegation.h"
2224c8dbbbSDavid Howells #include "internal.h"
231da177e4SLinus Torvalds 
24905f8d16STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation)
25905f8d16STrond Myklebust {
26e00b8a24STrond Myklebust 	if (delegation->cred) {
27e00b8a24STrond Myklebust 		put_rpccred(delegation->cred);
28e00b8a24STrond Myklebust 		delegation->cred = NULL;
29e00b8a24STrond Myklebust 	}
3026f04ddeSLai Jiangshan 	kfree_rcu(delegation, rcu);
318383e460STrond Myklebust }
328383e460STrond Myklebust 
33d3978bb3SChuck Lever /**
34d3978bb3SChuck Lever  * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
35d3978bb3SChuck Lever  * @delegation: delegation to process
36d3978bb3SChuck Lever  *
37d3978bb3SChuck Lever  */
38b7391f44STrond Myklebust void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
39b7391f44STrond Myklebust {
40b7391f44STrond Myklebust 	set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
41b7391f44STrond Myklebust }
42b7391f44STrond Myklebust 
43d3978bb3SChuck Lever /**
44d3978bb3SChuck Lever  * nfs_have_delegation - check if inode has a delegation
45d3978bb3SChuck Lever  * @inode: inode to check
46d3978bb3SChuck Lever  * @flags: delegation types to check for
47d3978bb3SChuck Lever  *
48d3978bb3SChuck Lever  * Returns one if inode has the indicated delegation, otherwise zero.
49d3978bb3SChuck Lever  */
50011e2a7fSBryan Schumaker int nfs4_have_delegation(struct inode *inode, fmode_t flags)
51b7391f44STrond Myklebust {
52b7391f44STrond Myklebust 	struct nfs_delegation *delegation;
53b7391f44STrond Myklebust 	int ret = 0;
54b7391f44STrond Myklebust 
55b7391f44STrond Myklebust 	flags &= FMODE_READ|FMODE_WRITE;
56b7391f44STrond Myklebust 	rcu_read_lock();
57b7391f44STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
58d25be546STrond Myklebust 	if (delegation != NULL && (delegation->type & flags) == flags &&
59d25be546STrond Myklebust 	    !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
60b7391f44STrond Myklebust 		nfs_mark_delegation_referenced(delegation);
61b7391f44STrond Myklebust 		ret = 1;
62b7391f44STrond Myklebust 	}
63b7391f44STrond Myklebust 	rcu_read_unlock();
64b7391f44STrond Myklebust 	return ret;
65b7391f44STrond Myklebust }
66b7391f44STrond Myklebust 
67db4f2e63STrond Myklebust static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
68888e694cSTrond Myklebust {
69888e694cSTrond Myklebust 	struct inode *inode = state->inode;
70888e694cSTrond Myklebust 	struct file_lock *fl;
71d5122201STrond Myklebust 	int status = 0;
72888e694cSTrond Myklebust 
733f09df70STrond Myklebust 	if (inode->i_flock == NULL)
7465b62a29STrond Myklebust 		return 0;
753f09df70STrond Myklebust 
7665b62a29STrond Myklebust 	if (inode->i_flock == NULL)
7765b62a29STrond Myklebust 		goto out;
78b89f4321SArnd Bergmann 	/* Protect inode->i_flock using the file locks lock */
79b89f4321SArnd Bergmann 	lock_flocks();
8090dc7d27SHarvey Harrison 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
81888e694cSTrond Myklebust 		if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
82888e694cSTrond Myklebust 			continue;
83cd3758e3STrond Myklebust 		if (nfs_file_open_context(fl->fl_file) != ctx)
84888e694cSTrond Myklebust 			continue;
85b89f4321SArnd Bergmann 		unlock_flocks();
86db4f2e63STrond Myklebust 		status = nfs4_lock_delegation_recall(fl, state, stateid);
87d5122201STrond Myklebust 		if (status < 0)
883f09df70STrond Myklebust 			goto out;
89b89f4321SArnd Bergmann 		lock_flocks();
90888e694cSTrond Myklebust 	}
91b89f4321SArnd Bergmann 	unlock_flocks();
923f09df70STrond Myklebust out:
93888e694cSTrond Myklebust 	return status;
94888e694cSTrond Myklebust }
95888e694cSTrond Myklebust 
96d18cc1fdSTrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
971da177e4SLinus Torvalds {
981da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
991da177e4SLinus Torvalds 	struct nfs_open_context *ctx;
100d25be546STrond Myklebust 	struct nfs4_state_owner *sp;
1011da177e4SLinus Torvalds 	struct nfs4_state *state;
102d25be546STrond Myklebust 	unsigned int seq;
103888e694cSTrond Myklebust 	int err;
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds again:
1061da177e4SLinus Torvalds 	spin_lock(&inode->i_lock);
1071da177e4SLinus Torvalds 	list_for_each_entry(ctx, &nfsi->open_files, list) {
1081da177e4SLinus Torvalds 		state = ctx->state;
1091da177e4SLinus Torvalds 		if (state == NULL)
1101da177e4SLinus Torvalds 			continue;
1111da177e4SLinus Torvalds 		if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
1121da177e4SLinus Torvalds 			continue;
113f597c537STrond Myklebust 		if (!nfs4_stateid_match(&state->stateid, stateid))
11490163027STrond Myklebust 			continue;
1151da177e4SLinus Torvalds 		get_nfs_open_context(ctx);
1161da177e4SLinus Torvalds 		spin_unlock(&inode->i_lock);
117d25be546STrond Myklebust 		sp = state->owner;
11865b62a29STrond Myklebust 		/* Block nfs4_proc_unlck */
11965b62a29STrond Myklebust 		mutex_lock(&sp->so_delegreturn_mutex);
120d25be546STrond Myklebust 		seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
12113437e12STrond Myklebust 		err = nfs4_open_delegation_recall(ctx, state, stateid);
122d25be546STrond Myklebust 		if (!err)
123db4f2e63STrond Myklebust 			err = nfs_delegation_claim_locks(ctx, state, stateid);
124d25be546STrond Myklebust 		if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
125d25be546STrond Myklebust 			err = -EAGAIN;
12665b62a29STrond Myklebust 		mutex_unlock(&sp->so_delegreturn_mutex);
1271da177e4SLinus Torvalds 		put_nfs_open_context(ctx);
128888e694cSTrond Myklebust 		if (err != 0)
129d18cc1fdSTrond Myklebust 			return err;
1301da177e4SLinus Torvalds 		goto again;
1311da177e4SLinus Torvalds 	}
1321da177e4SLinus Torvalds 	spin_unlock(&inode->i_lock);
133d18cc1fdSTrond Myklebust 	return 0;
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds 
136d3978bb3SChuck Lever /**
137d3978bb3SChuck Lever  * nfs_inode_reclaim_delegation - process a delegation reclaim request
138d3978bb3SChuck Lever  * @inode: inode to process
139d3978bb3SChuck Lever  * @cred: credential to use for request
140d3978bb3SChuck Lever  * @res: new delegation state from server
141d3978bb3SChuck Lever  *
1421da177e4SLinus Torvalds  */
143d3978bb3SChuck Lever void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
144d3978bb3SChuck Lever 				  struct nfs_openres *res)
1451da177e4SLinus Torvalds {
1468f649c37STrond Myklebust 	struct nfs_delegation *delegation;
1478f649c37STrond Myklebust 	struct rpc_cred *oldcred = NULL;
1481da177e4SLinus Torvalds 
1498f649c37STrond Myklebust 	rcu_read_lock();
1508f649c37STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
1518f649c37STrond Myklebust 	if (delegation != NULL) {
1528f649c37STrond Myklebust 		spin_lock(&delegation->lock);
1538f649c37STrond Myklebust 		if (delegation->inode != NULL) {
154f597c537STrond Myklebust 			nfs4_stateid_copy(&delegation->stateid, &res->delegation);
1551da177e4SLinus Torvalds 			delegation->type = res->delegation_type;
1561da177e4SLinus Torvalds 			delegation->maxsize = res->maxsize;
15705c88babSTrond Myklebust 			oldcred = delegation->cred;
1581da177e4SLinus Torvalds 			delegation->cred = get_rpccred(cred);
1598f649c37STrond Myklebust 			clear_bit(NFS_DELEGATION_NEED_RECLAIM,
1608f649c37STrond Myklebust 				  &delegation->flags);
1611da177e4SLinus Torvalds 			NFS_I(inode)->delegation_state = delegation->type;
1628f649c37STrond Myklebust 			spin_unlock(&delegation->lock);
16305c88babSTrond Myklebust 			put_rpccred(oldcred);
1648f649c37STrond Myklebust 			rcu_read_unlock();
1658f649c37STrond Myklebust 		} else {
1668f649c37STrond Myklebust 			/* We appear to have raced with a delegation return. */
1678f649c37STrond Myklebust 			spin_unlock(&delegation->lock);
1688f649c37STrond Myklebust 			rcu_read_unlock();
1698f649c37STrond Myklebust 			nfs_inode_set_delegation(inode, cred, res);
1708f649c37STrond Myklebust 		}
1718f649c37STrond Myklebust 	} else {
1728f649c37STrond Myklebust 		rcu_read_unlock();
1738f649c37STrond Myklebust 	}
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds 
17657bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
17757bfa891STrond Myklebust {
17857bfa891STrond Myklebust 	int res = 0;
17957bfa891STrond Myklebust 
18057bfa891STrond Myklebust 	res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
18157bfa891STrond Myklebust 	nfs_free_delegation(delegation);
18257bfa891STrond Myklebust 	return res;
18357bfa891STrond Myklebust }
18457bfa891STrond Myklebust 
18586e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
18686e89489STrond Myklebust {
18786e89489STrond Myklebust 	struct inode *inode = NULL;
18886e89489STrond Myklebust 
18986e89489STrond Myklebust 	spin_lock(&delegation->lock);
19086e89489STrond Myklebust 	if (delegation->inode != NULL)
19186e89489STrond Myklebust 		inode = igrab(delegation->inode);
19286e89489STrond Myklebust 	spin_unlock(&delegation->lock);
19386e89489STrond Myklebust 	return inode;
19486e89489STrond Myklebust }
19586e89489STrond Myklebust 
196dda4b225SChuck Lever static struct nfs_delegation *
197d25be546STrond Myklebust nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
19857bfa891STrond Myklebust {
199d25be546STrond Myklebust 	struct nfs_delegation *ret = NULL;
200d25be546STrond Myklebust 	struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
20157bfa891STrond Myklebust 
20257bfa891STrond Myklebust 	if (delegation == NULL)
203d25be546STrond Myklebust 		goto out;
204d25be546STrond Myklebust 	spin_lock(&delegation->lock);
205d25be546STrond Myklebust 	if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
206d25be546STrond Myklebust 		ret = delegation;
207d25be546STrond Myklebust 	spin_unlock(&delegation->lock);
208d25be546STrond Myklebust out:
209d25be546STrond Myklebust 	return ret;
210d25be546STrond Myklebust }
211d25be546STrond Myklebust 
212d25be546STrond Myklebust static struct nfs_delegation *
213d25be546STrond Myklebust nfs_start_delegation_return(struct nfs_inode *nfsi)
214d25be546STrond Myklebust {
215d25be546STrond Myklebust 	struct nfs_delegation *delegation;
216d25be546STrond Myklebust 
217d25be546STrond Myklebust 	rcu_read_lock();
218d25be546STrond Myklebust 	delegation = nfs_start_delegation_return_locked(nfsi);
219d25be546STrond Myklebust 	rcu_read_unlock();
220d25be546STrond Myklebust 	return delegation;
221d25be546STrond Myklebust }
222d25be546STrond Myklebust 
223d25be546STrond Myklebust static void
224d25be546STrond Myklebust nfs_abort_delegation_return(struct nfs_delegation *delegation,
225d25be546STrond Myklebust 		struct nfs_client *clp)
226d25be546STrond Myklebust {
227dda4b225SChuck Lever 
22834310430STrond Myklebust 	spin_lock(&delegation->lock);
229d25be546STrond Myklebust 	clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
230d25be546STrond Myklebust 	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
231d25be546STrond Myklebust 	spin_unlock(&delegation->lock);
232d25be546STrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
233d25be546STrond Myklebust }
234d25be546STrond Myklebust 
235d25be546STrond Myklebust static struct nfs_delegation *
236d25be546STrond Myklebust nfs_detach_delegation_locked(struct nfs_inode *nfsi,
237d25be546STrond Myklebust 		struct nfs_delegation *delegation,
238d25be546STrond Myklebust 		struct nfs_client *clp)
239d25be546STrond Myklebust {
240d25be546STrond Myklebust 	struct nfs_delegation *deleg_cur =
241d25be546STrond Myklebust 		rcu_dereference_protected(nfsi->delegation,
242d25be546STrond Myklebust 				lockdep_is_held(&clp->cl_lock));
243d25be546STrond Myklebust 
244d25be546STrond Myklebust 	if (deleg_cur == NULL || delegation != deleg_cur)
245d25be546STrond Myklebust 		return NULL;
246d25be546STrond Myklebust 
247d25be546STrond Myklebust 	spin_lock(&delegation->lock);
248d25be546STrond Myklebust 	set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
24957bfa891STrond Myklebust 	list_del_rcu(&delegation->super_list);
25086e89489STrond Myklebust 	delegation->inode = NULL;
25157bfa891STrond Myklebust 	nfsi->delegation_state = 0;
25257bfa891STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, NULL);
25334310430STrond Myklebust 	spin_unlock(&delegation->lock);
25457bfa891STrond Myklebust 	return delegation;
25557bfa891STrond Myklebust }
25657bfa891STrond Myklebust 
257dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
258d25be546STrond Myklebust 		struct nfs_delegation *delegation,
259d3978bb3SChuck Lever 		struct nfs_server *server)
260dda4b225SChuck Lever {
261d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
262dda4b225SChuck Lever 
263dda4b225SChuck Lever 	spin_lock(&clp->cl_lock);
264d25be546STrond Myklebust 	delegation = nfs_detach_delegation_locked(nfsi, delegation, clp);
265dda4b225SChuck Lever 	spin_unlock(&clp->cl_lock);
266dda4b225SChuck Lever 	return delegation;
267dda4b225SChuck Lever }
268dda4b225SChuck Lever 
269d25be546STrond Myklebust static struct nfs_delegation *
270d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode)
271d25be546STrond Myklebust {
272d25be546STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
273d25be546STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
274d25be546STrond Myklebust 	struct nfs_delegation *delegation;
275d25be546STrond Myklebust 
276d25be546STrond Myklebust 	delegation = nfs_start_delegation_return(nfsi);
277d25be546STrond Myklebust 	if (delegation == NULL)
278d25be546STrond Myklebust 		return NULL;
279d25be546STrond Myklebust 	return nfs_detach_delegation(nfsi, delegation, server);
280d25be546STrond Myklebust }
281d25be546STrond Myklebust 
282d3978bb3SChuck Lever /**
283d3978bb3SChuck Lever  * nfs_inode_set_delegation - set up a delegation on an inode
284d3978bb3SChuck Lever  * @inode: inode to which delegation applies
285d3978bb3SChuck Lever  * @cred: cred to use for subsequent delegation processing
286d3978bb3SChuck Lever  * @res: new delegation state from server
287d3978bb3SChuck Lever  *
288d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
2891da177e4SLinus Torvalds  */
2901da177e4SLinus Torvalds int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
2911da177e4SLinus Torvalds {
292d3978bb3SChuck Lever 	struct nfs_server *server = NFS_SERVER(inode);
293d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
2941da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
29517d2c0a0SDavid Howells 	struct nfs_delegation *delegation, *old_delegation;
29657bfa891STrond Myklebust 	struct nfs_delegation *freeme = NULL;
2971da177e4SLinus Torvalds 	int status = 0;
2981da177e4SLinus Torvalds 
2998535b2beSTrond Myklebust 	delegation = kmalloc(sizeof(*delegation), GFP_NOFS);
3001da177e4SLinus Torvalds 	if (delegation == NULL)
3011da177e4SLinus Torvalds 		return -ENOMEM;
302f597c537STrond Myklebust 	nfs4_stateid_copy(&delegation->stateid, &res->delegation);
3031da177e4SLinus Torvalds 	delegation->type = res->delegation_type;
3041da177e4SLinus Torvalds 	delegation->maxsize = res->maxsize;
305a9a4a87aSTrond Myklebust 	delegation->change_attr = inode->i_version;
3061da177e4SLinus Torvalds 	delegation->cred = get_rpccred(cred);
3071da177e4SLinus Torvalds 	delegation->inode = inode;
308b7391f44STrond Myklebust 	delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
30934310430STrond Myklebust 	spin_lock_init(&delegation->lock);
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
31217d2c0a0SDavid Howells 	old_delegation = rcu_dereference_protected(nfsi->delegation,
31317d2c0a0SDavid Howells 					lockdep_is_held(&clp->cl_lock));
31417d2c0a0SDavid Howells 	if (old_delegation != NULL) {
315f597c537STrond Myklebust 		if (nfs4_stateid_match(&delegation->stateid,
316f597c537STrond Myklebust 					&old_delegation->stateid) &&
31717d2c0a0SDavid Howells 				delegation->type == old_delegation->type) {
31857bfa891STrond Myklebust 			goto out;
31957bfa891STrond Myklebust 		}
32057bfa891STrond Myklebust 		/*
32157bfa891STrond Myklebust 		 * Deal with broken servers that hand out two
32257bfa891STrond Myklebust 		 * delegations for the same file.
32317280175STrond Myklebust 		 * Allow for upgrades to a WRITE delegation, but
32417280175STrond Myklebust 		 * nothing else.
32557bfa891STrond Myklebust 		 */
32657bfa891STrond Myklebust 		dfprintk(FILE, "%s: server %s handed out "
32757bfa891STrond Myklebust 				"a duplicate delegation!\n",
3283110ff80SHarvey Harrison 				__func__, clp->cl_hostname);
32917280175STrond Myklebust 		if (delegation->type == old_delegation->type ||
33017280175STrond Myklebust 		    !(delegation->type & FMODE_WRITE)) {
33157bfa891STrond Myklebust 			freeme = delegation;
33257bfa891STrond Myklebust 			delegation = NULL;
33357bfa891STrond Myklebust 			goto out;
33457bfa891STrond Myklebust 		}
335d25be546STrond Myklebust 		freeme = nfs_detach_delegation_locked(nfsi,
336d25be546STrond Myklebust 				old_delegation, clp);
337d25be546STrond Myklebust 		if (freeme == NULL)
338d25be546STrond Myklebust 			goto out;
33957bfa891STrond Myklebust 	}
340d3978bb3SChuck Lever 	list_add_rcu(&delegation->super_list, &server->delegations);
3411da177e4SLinus Torvalds 	nfsi->delegation_state = delegation->type;
3428383e460STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, delegation);
3431da177e4SLinus Torvalds 	delegation = NULL;
344412c77ceSTrond Myklebust 
345412c77ceSTrond Myklebust 	/* Ensure we revalidate the attributes and page cache! */
346412c77ceSTrond Myklebust 	spin_lock(&inode->i_lock);
347412c77ceSTrond Myklebust 	nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
348412c77ceSTrond Myklebust 	spin_unlock(&inode->i_lock);
349412c77ceSTrond Myklebust 
35057bfa891STrond Myklebust out:
3511da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
352603c83daSTrond Myklebust 	if (delegation != NULL)
353603c83daSTrond Myklebust 		nfs_free_delegation(delegation);
35457bfa891STrond Myklebust 	if (freeme != NULL)
35557bfa891STrond Myklebust 		nfs_do_return_delegation(inode, freeme, 0);
3561da177e4SLinus Torvalds 	return status;
3571da177e4SLinus Torvalds }
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds /*
3601da177e4SLinus Torvalds  * Basic procedure for returning a delegation to the server
3611da177e4SLinus Torvalds  */
362d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
3631da177e4SLinus Torvalds {
364d25be546STrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
3651da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
366d18cc1fdSTrond Myklebust 	int err;
3671da177e4SLinus Torvalds 
368d25be546STrond Myklebust 	if (delegation == NULL)
369d25be546STrond Myklebust 		return 0;
370d25be546STrond Myklebust 	do {
371d18cc1fdSTrond Myklebust 		err = nfs_delegation_claim_opens(inode, &delegation->stateid);
372d25be546STrond Myklebust 		if (!issync || err != -EAGAIN)
373d25be546STrond Myklebust 			break;
374d25be546STrond Myklebust 		/*
375d25be546STrond Myklebust 		 * Guard against state recovery
376d25be546STrond Myklebust 		 */
377d25be546STrond Myklebust 		err = nfs4_wait_clnt_recover(clp);
378d25be546STrond Myklebust 	} while (err == 0);
379d25be546STrond Myklebust 
380d25be546STrond Myklebust 	if (err) {
381d25be546STrond Myklebust 		nfs_abort_delegation_return(delegation, clp);
382d25be546STrond Myklebust 		goto out;
383d25be546STrond Myklebust 	}
384d25be546STrond Myklebust 	if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode)))
385d18cc1fdSTrond Myklebust 		goto out;
3861da177e4SLinus Torvalds 
387d18cc1fdSTrond Myklebust 	err = nfs_do_return_delegation(inode, delegation, issync);
388d18cc1fdSTrond Myklebust out:
389d18cc1fdSTrond Myklebust 	return err;
39090163027STrond Myklebust }
39190163027STrond Myklebust 
392d3978bb3SChuck Lever /**
393d3978bb3SChuck Lever  * nfs_client_return_marked_delegations - return previously marked delegations
394d3978bb3SChuck Lever  * @clp: nfs_client to process
395d3978bb3SChuck Lever  *
396dc327ed4STrond Myklebust  * Note that this function is designed to be called by the state
397dc327ed4STrond Myklebust  * manager thread. For this reason, it cannot flush the dirty data,
398dc327ed4STrond Myklebust  * since that could deadlock in case of a state recovery error.
399dc327ed4STrond Myklebust  *
400d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
401515d8611STrond Myklebust  */
402d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp)
403515d8611STrond Myklebust {
404515d8611STrond Myklebust 	struct nfs_delegation *delegation;
405d3978bb3SChuck Lever 	struct nfs_server *server;
406515d8611STrond Myklebust 	struct inode *inode;
407d18cc1fdSTrond Myklebust 	int err = 0;
408515d8611STrond Myklebust 
409515d8611STrond Myklebust restart:
410515d8611STrond Myklebust 	rcu_read_lock();
411d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
412d3978bb3SChuck Lever 		list_for_each_entry_rcu(delegation, &server->delegations,
413d3978bb3SChuck Lever 								super_list) {
414d3978bb3SChuck Lever 			if (!test_and_clear_bit(NFS_DELEGATION_RETURN,
415d3978bb3SChuck Lever 							&delegation->flags))
416515d8611STrond Myklebust 				continue;
417515d8611STrond Myklebust 			inode = nfs_delegation_grab_inode(delegation);
418515d8611STrond Myklebust 			if (inode == NULL)
419515d8611STrond Myklebust 				continue;
420d25be546STrond Myklebust 			delegation = nfs_start_delegation_return_locked(NFS_I(inode));
421515d8611STrond Myklebust 			rcu_read_unlock();
422d3978bb3SChuck Lever 
423d25be546STrond Myklebust 			err = nfs_end_delegation_return(inode, delegation, 0);
424515d8611STrond Myklebust 			iput(inode);
425d18cc1fdSTrond Myklebust 			if (!err)
426515d8611STrond Myklebust 				goto restart;
427d18cc1fdSTrond Myklebust 			set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
428d18cc1fdSTrond Myklebust 			return err;
429515d8611STrond Myklebust 		}
430d3978bb3SChuck Lever 	}
431515d8611STrond Myklebust 	rcu_read_unlock();
432d18cc1fdSTrond Myklebust 	return 0;
433515d8611STrond Myklebust }
434515d8611STrond Myklebust 
435d3978bb3SChuck Lever /**
436d3978bb3SChuck Lever  * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens
437d3978bb3SChuck Lever  * @inode: inode to process
438d3978bb3SChuck Lever  *
439d3978bb3SChuck Lever  * Does not protect against delegation reclaims, therefore really only safe
440d3978bb3SChuck Lever  * to be called from nfs4_clear_inode().
441e6f81075STrond Myklebust  */
442e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode)
443e6f81075STrond Myklebust {
444e6f81075STrond Myklebust 	struct nfs_delegation *delegation;
445e6f81075STrond Myklebust 
446d25be546STrond Myklebust 	delegation = nfs_inode_detach_delegation(inode);
447e6f81075STrond Myklebust 	if (delegation != NULL)
448e6f81075STrond Myklebust 		nfs_do_return_delegation(inode, delegation, 0);
449e6f81075STrond Myklebust }
450e6f81075STrond Myklebust 
451d3978bb3SChuck Lever /**
452d3978bb3SChuck Lever  * nfs_inode_return_delegation - synchronously return a delegation
453d3978bb3SChuck Lever  * @inode: inode to process
454d3978bb3SChuck Lever  *
455c57d1bc5STrond Myklebust  * This routine will always flush any dirty data to disk on the
456c57d1bc5STrond Myklebust  * assumption that if we need to return the delegation, then
457c57d1bc5STrond Myklebust  * we should stop caching.
458c57d1bc5STrond Myklebust  *
459d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
460d3978bb3SChuck Lever  */
46157ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode)
46290163027STrond Myklebust {
46390163027STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
46490163027STrond Myklebust 	struct nfs_delegation *delegation;
46590163027STrond Myklebust 	int err = 0;
46690163027STrond Myklebust 
467c57d1bc5STrond Myklebust 	nfs_wb_all(inode);
468d25be546STrond Myklebust 	delegation = nfs_start_delegation_return(nfsi);
469d25be546STrond Myklebust 	if (delegation != NULL)
470d25be546STrond Myklebust 		err = nfs_end_delegation_return(inode, delegation, 1);
47190163027STrond Myklebust 	return err;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds 
474ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server,
475ed1e6211STrond Myklebust 		struct nfs_delegation *delegation)
4766411bd4aSTrond Myklebust {
4776411bd4aSTrond Myklebust 	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
478ed1e6211STrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
4796411bd4aSTrond Myklebust }
4806411bd4aSTrond Myklebust 
481d3978bb3SChuck Lever /**
482d3978bb3SChuck Lever  * nfs_super_return_all_delegations - return delegations for one superblock
483d3978bb3SChuck Lever  * @sb: sb to process
484d3978bb3SChuck Lever  *
4851da177e4SLinus Torvalds  */
486eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server)
4871da177e4SLinus Torvalds {
488d3978bb3SChuck Lever 	struct nfs_client *clp = server->nfs_client;
4891da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds 	if (clp == NULL)
4921da177e4SLinus Torvalds 		return;
493d3978bb3SChuck Lever 
4948383e460STrond Myklebust 	rcu_read_lock();
495d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
49686e89489STrond Myklebust 		spin_lock(&delegation->lock);
497515d8611STrond Myklebust 		set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
49886e89489STrond Myklebust 		spin_unlock(&delegation->lock);
4991da177e4SLinus Torvalds 	}
5008383e460STrond Myklebust 	rcu_read_unlock();
501d3978bb3SChuck Lever 
502d18cc1fdSTrond Myklebust 	if (nfs_client_return_marked_delegations(clp) != 0)
503d18cc1fdSTrond Myklebust 		nfs4_schedule_state_manager(clp);
504515d8611STrond Myklebust }
505515d8611STrond Myklebust 
506d3978bb3SChuck Lever static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
507d3978bb3SChuck Lever 						 fmode_t flags)
508515d8611STrond Myklebust {
509515d8611STrond Myklebust 	struct nfs_delegation *delegation;
510515d8611STrond Myklebust 
511d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
512c79571a5SAlexandros Batsakis 		if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
513c79571a5SAlexandros Batsakis 			continue;
514c79571a5SAlexandros Batsakis 		if (delegation->type & flags)
515ed1e6211STrond Myklebust 			nfs_mark_return_delegation(server, delegation);
516707fb4b3STrond Myklebust 	}
517d3978bb3SChuck Lever }
518d3978bb3SChuck Lever 
519d3978bb3SChuck Lever static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp,
520d3978bb3SChuck Lever 							fmode_t flags)
521d3978bb3SChuck Lever {
522d3978bb3SChuck Lever 	struct nfs_server *server;
523d3978bb3SChuck Lever 
524d3978bb3SChuck Lever 	rcu_read_lock();
525d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
526d3978bb3SChuck Lever 		nfs_mark_return_all_delegation_types(server, flags);
527515d8611STrond Myklebust 	rcu_read_unlock();
5281da177e4SLinus Torvalds }
5291da177e4SLinus Torvalds 
530b0d3ded1STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp)
53158d9714aSTrond Myklebust {
532b0d3ded1STrond Myklebust 	if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
533b0d3ded1STrond Myklebust 		nfs4_schedule_state_manager(clp);
53458d9714aSTrond Myklebust }
53558d9714aSTrond Myklebust 
536a1d0b5eeSTrond Myklebust void nfs_remove_bad_delegation(struct inode *inode)
537a1d0b5eeSTrond Myklebust {
538a1d0b5eeSTrond Myklebust 	struct nfs_delegation *delegation;
539a1d0b5eeSTrond Myklebust 
540d25be546STrond Myklebust 	delegation = nfs_inode_detach_delegation(inode);
541a1d0b5eeSTrond Myklebust 	if (delegation) {
542a1d0b5eeSTrond Myklebust 		nfs_inode_find_state_and_recover(inode, &delegation->stateid);
543a1d0b5eeSTrond Myklebust 		nfs_free_delegation(delegation);
544a1d0b5eeSTrond Myklebust 	}
545a1d0b5eeSTrond Myklebust }
5469cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
547a1d0b5eeSTrond Myklebust 
548d3978bb3SChuck Lever /**
549d3978bb3SChuck Lever  * nfs_expire_all_delegation_types
550d3978bb3SChuck Lever  * @clp: client to process
551d3978bb3SChuck Lever  * @flags: delegation types to expire
552d3978bb3SChuck Lever  *
553d3978bb3SChuck Lever  */
55431f09607SAlexandros Batsakis void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags)
555c79571a5SAlexandros Batsakis {
556c79571a5SAlexandros Batsakis 	nfs_client_mark_return_all_delegation_types(clp, flags);
557c79571a5SAlexandros Batsakis 	nfs_delegation_run_state_manager(clp);
558c79571a5SAlexandros Batsakis }
559c79571a5SAlexandros Batsakis 
560d3978bb3SChuck Lever /**
561d3978bb3SChuck Lever  * nfs_expire_all_delegations
562d3978bb3SChuck Lever  * @clp: client to process
563d3978bb3SChuck Lever  *
564d3978bb3SChuck Lever  */
565adfa6f98SDavid Howells void nfs_expire_all_delegations(struct nfs_client *clp)
56658d9714aSTrond Myklebust {
567c79571a5SAlexandros Batsakis 	nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
56858d9714aSTrond Myklebust }
56958d9714aSTrond Myklebust 
570d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
571b7391f44STrond Myklebust {
572b7391f44STrond Myklebust 	struct nfs_delegation *delegation;
573b7391f44STrond Myklebust 
574d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
575b7391f44STrond Myklebust 		if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
576b7391f44STrond Myklebust 			continue;
577ed1e6211STrond Myklebust 		nfs_mark_return_delegation(server, delegation);
578b7391f44STrond Myklebust 	}
579b7391f44STrond Myklebust }
580b7391f44STrond Myklebust 
581d3978bb3SChuck Lever /**
582d3978bb3SChuck Lever  * nfs_expire_unreferenced_delegations - Eliminate unused delegations
583d3978bb3SChuck Lever  * @clp: nfs_client to process
584d3978bb3SChuck Lever  *
585d3978bb3SChuck Lever  */
586b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
587b7391f44STrond Myklebust {
588d3978bb3SChuck Lever 	struct nfs_server *server;
589d3978bb3SChuck Lever 
590d3978bb3SChuck Lever 	rcu_read_lock();
591d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
592d3978bb3SChuck Lever 		nfs_mark_return_unreferenced_delegations(server);
593d3978bb3SChuck Lever 	rcu_read_unlock();
594d3978bb3SChuck Lever 
595b7391f44STrond Myklebust 	nfs_delegation_run_state_manager(clp);
596b7391f44STrond Myklebust }
597b7391f44STrond Myklebust 
598d3978bb3SChuck Lever /**
599d3978bb3SChuck Lever  * nfs_async_inode_return_delegation - asynchronously return a delegation
600d3978bb3SChuck Lever  * @inode: inode to process
6018e663f0eSTrond Myklebust  * @stateid: state ID information
602d3978bb3SChuck Lever  *
603d3978bb3SChuck Lever  * Returns zero on success, or a negative errno value.
6041da177e4SLinus Torvalds  */
605d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode,
606d3978bb3SChuck Lever 				      const nfs4_stateid *stateid)
6071da177e4SLinus Torvalds {
608ed1e6211STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
609ed1e6211STrond Myklebust 	struct nfs_client *clp = server->nfs_client;
6106411bd4aSTrond Myklebust 	struct nfs_delegation *delegation;
6111da177e4SLinus Torvalds 
612dc327ed4STrond Myklebust 	filemap_flush(inode->i_mapping);
613dc327ed4STrond Myklebust 
6146411bd4aSTrond Myklebust 	rcu_read_lock();
6156411bd4aSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
6162597641dSAlexandros Batsakis 
61736281caaSTrond Myklebust 	if (!clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) {
6186411bd4aSTrond Myklebust 		rcu_read_unlock();
6196411bd4aSTrond Myklebust 		return -ENOENT;
6206411bd4aSTrond Myklebust 	}
621ed1e6211STrond Myklebust 	nfs_mark_return_delegation(server, delegation);
6226411bd4aSTrond Myklebust 	rcu_read_unlock();
623d3978bb3SChuck Lever 
6246411bd4aSTrond Myklebust 	nfs_delegation_run_state_manager(clp);
6256411bd4aSTrond Myklebust 	return 0;
6261da177e4SLinus Torvalds }
6271da177e4SLinus Torvalds 
628d3978bb3SChuck Lever static struct inode *
629d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server,
630d3978bb3SChuck Lever 				 const struct nfs_fh *fhandle)
6311da177e4SLinus Torvalds {
6321da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
6331da177e4SLinus Torvalds 	struct inode *res = NULL;
634d3978bb3SChuck Lever 
635d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
63686e89489STrond Myklebust 		spin_lock(&delegation->lock);
63786e89489STrond Myklebust 		if (delegation->inode != NULL &&
63886e89489STrond Myklebust 		    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
6391da177e4SLinus Torvalds 			res = igrab(delegation->inode);
6401da177e4SLinus Torvalds 		}
64186e89489STrond Myklebust 		spin_unlock(&delegation->lock);
64286e89489STrond Myklebust 		if (res != NULL)
64386e89489STrond Myklebust 			break;
6441da177e4SLinus Torvalds 	}
645d3978bb3SChuck Lever 	return res;
646d3978bb3SChuck Lever }
647d3978bb3SChuck Lever 
648d3978bb3SChuck Lever /**
649d3978bb3SChuck Lever  * nfs_delegation_find_inode - retrieve the inode associated with a delegation
650d3978bb3SChuck Lever  * @clp: client state handle
651d3978bb3SChuck Lever  * @fhandle: filehandle from a delegation recall
652d3978bb3SChuck Lever  *
653d3978bb3SChuck Lever  * Returns pointer to inode matching "fhandle," or NULL if a matching inode
654d3978bb3SChuck Lever  * cannot be found.
655d3978bb3SChuck Lever  */
656d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
657d3978bb3SChuck Lever 					const struct nfs_fh *fhandle)
658d3978bb3SChuck Lever {
659d3978bb3SChuck Lever 	struct nfs_server *server;
660d3978bb3SChuck Lever 	struct inode *res = NULL;
661d3978bb3SChuck Lever 
662d3978bb3SChuck Lever 	rcu_read_lock();
663d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
664d3978bb3SChuck Lever 		res = nfs_delegation_find_inode_server(server, fhandle);
665d3978bb3SChuck Lever 		if (res != NULL)
666d3978bb3SChuck Lever 			break;
667d3978bb3SChuck Lever 	}
6688383e460STrond Myklebust 	rcu_read_unlock();
6691da177e4SLinus Torvalds 	return res;
6701da177e4SLinus Torvalds }
6711da177e4SLinus Torvalds 
672d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
673d3978bb3SChuck Lever {
674d3978bb3SChuck Lever 	struct nfs_delegation *delegation;
675d3978bb3SChuck Lever 
676d3978bb3SChuck Lever 	list_for_each_entry_rcu(delegation, &server->delegations, super_list)
677d3978bb3SChuck Lever 		set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
678d3978bb3SChuck Lever }
679d3978bb3SChuck Lever 
680d3978bb3SChuck Lever /**
681d3978bb3SChuck Lever  * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed
682d3978bb3SChuck Lever  * @clp: nfs_client to process
683d3978bb3SChuck Lever  *
6841da177e4SLinus Torvalds  */
685adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp)
6861da177e4SLinus Torvalds {
687d3978bb3SChuck Lever 	struct nfs_server *server;
688d3978bb3SChuck Lever 
6898383e460STrond Myklebust 	rcu_read_lock();
690d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
691d3978bb3SChuck Lever 		nfs_delegation_mark_reclaim_server(server);
6928383e460STrond Myklebust 	rcu_read_unlock();
6931da177e4SLinus Torvalds }
6941da177e4SLinus Torvalds 
695d3978bb3SChuck Lever /**
696d3978bb3SChuck Lever  * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done
697d3978bb3SChuck Lever  * @clp: nfs_client to process
698d3978bb3SChuck Lever  *
6991da177e4SLinus Torvalds  */
700adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
7011da177e4SLinus Torvalds {
7028383e460STrond Myklebust 	struct nfs_delegation *delegation;
703d3978bb3SChuck Lever 	struct nfs_server *server;
70486e89489STrond Myklebust 	struct inode *inode;
705d3978bb3SChuck Lever 
7068383e460STrond Myklebust restart:
7078383e460STrond Myklebust 	rcu_read_lock();
708d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
709d3978bb3SChuck Lever 		list_for_each_entry_rcu(delegation, &server->delegations,
710d3978bb3SChuck Lever 								super_list) {
711d3978bb3SChuck Lever 			if (test_bit(NFS_DELEGATION_NEED_RECLAIM,
712d3978bb3SChuck Lever 						&delegation->flags) == 0)
7131da177e4SLinus Torvalds 				continue;
71486e89489STrond Myklebust 			inode = nfs_delegation_grab_inode(delegation);
71586e89489STrond Myklebust 			if (inode == NULL)
71686e89489STrond Myklebust 				continue;
717d3978bb3SChuck Lever 			delegation = nfs_detach_delegation(NFS_I(inode),
718d25be546STrond Myklebust 					delegation, server);
7198383e460STrond Myklebust 			rcu_read_unlock();
720d3978bb3SChuck Lever 
7218383e460STrond Myklebust 			if (delegation != NULL)
722905f8d16STrond Myklebust 				nfs_free_delegation(delegation);
72386e89489STrond Myklebust 			iput(inode);
7248383e460STrond Myklebust 			goto restart;
7251da177e4SLinus Torvalds 		}
726d3978bb3SChuck Lever 	}
7278383e460STrond Myklebust 	rcu_read_unlock();
7281da177e4SLinus Torvalds }
7293e4f6290STrond Myklebust 
730d3978bb3SChuck Lever /**
731d3978bb3SChuck Lever  * nfs_delegations_present - check for existence of delegations
732d3978bb3SChuck Lever  * @clp: client state handle
733d3978bb3SChuck Lever  *
734d3978bb3SChuck Lever  * Returns one if there are any nfs_delegation structures attached
735d3978bb3SChuck Lever  * to this nfs_client.
736d3978bb3SChuck Lever  */
737d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp)
738d3978bb3SChuck Lever {
739d3978bb3SChuck Lever 	struct nfs_server *server;
740d3978bb3SChuck Lever 	int ret = 0;
741d3978bb3SChuck Lever 
742d3978bb3SChuck Lever 	rcu_read_lock();
743d3978bb3SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
744d3978bb3SChuck Lever 		if (!list_empty(&server->delegations)) {
745d3978bb3SChuck Lever 			ret = 1;
746d3978bb3SChuck Lever 			break;
747d3978bb3SChuck Lever 		}
748d3978bb3SChuck Lever 	rcu_read_unlock();
749d3978bb3SChuck Lever 	return ret;
750d3978bb3SChuck Lever }
751d3978bb3SChuck Lever 
752d3978bb3SChuck Lever /**
753d3978bb3SChuck Lever  * nfs4_copy_delegation_stateid - Copy inode's state ID information
754d3978bb3SChuck Lever  * @dst: stateid data structure to fill in
755d3978bb3SChuck Lever  * @inode: inode to check
7560032a7a7STrond Myklebust  * @flags: delegation type requirement
757d3978bb3SChuck Lever  *
7580032a7a7STrond Myklebust  * Returns "true" and fills in "dst->data" * if inode had a delegation,
7590032a7a7STrond Myklebust  * otherwise "false" is returned.
760d3978bb3SChuck Lever  */
7610032a7a7STrond Myklebust bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode,
7620032a7a7STrond Myklebust 		fmode_t flags)
7633e4f6290STrond Myklebust {
7643e4f6290STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
7653e4f6290STrond Myklebust 	struct nfs_delegation *delegation;
7660032a7a7STrond Myklebust 	bool ret;
7673e4f6290STrond Myklebust 
7680032a7a7STrond Myklebust 	flags &= FMODE_READ|FMODE_WRITE;
7698383e460STrond Myklebust 	rcu_read_lock();
7708383e460STrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
7710032a7a7STrond Myklebust 	ret = (delegation != NULL && (delegation->type & flags) == flags);
7720032a7a7STrond Myklebust 	if (ret) {
773f597c537STrond Myklebust 		nfs4_stateid_copy(dst, &delegation->stateid);
7740032a7a7STrond Myklebust 		nfs_mark_delegation_referenced(delegation);
7753e4f6290STrond Myklebust 	}
7768383e460STrond Myklebust 	rcu_read_unlock();
7778383e460STrond Myklebust 	return ret;
7783e4f6290STrond Myklebust }
779