xref: /openbmc/linux/fs/nfs/delegation.c (revision 17d2c0a0)
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>
14405f5571SAlexey Dobriyan #include <linux/smp_lock.h>
151da177e4SLinus Torvalds #include <linux/spinlock.h>
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds #include <linux/nfs4.h>
181da177e4SLinus Torvalds #include <linux/nfs_fs.h>
191da177e4SLinus Torvalds #include <linux/nfs_xdr.h>
201da177e4SLinus Torvalds 
214ce79717STrond Myklebust #include "nfs4_fs.h"
221da177e4SLinus Torvalds #include "delegation.h"
2324c8dbbbSDavid Howells #include "internal.h"
241da177e4SLinus Torvalds 
25905f8d16STrond Myklebust static void nfs_do_free_delegation(struct nfs_delegation *delegation)
261da177e4SLinus Torvalds {
2717d2c0a0SDavid Howells 	if (delegation->cred)
2817d2c0a0SDavid Howells 		put_rpccred(delegation->cred);
291da177e4SLinus Torvalds 	kfree(delegation);
301da177e4SLinus Torvalds }
311da177e4SLinus Torvalds 
328383e460STrond Myklebust static void nfs_free_delegation_callback(struct rcu_head *head)
338383e460STrond Myklebust {
348383e460STrond Myklebust 	struct nfs_delegation *delegation = container_of(head, struct nfs_delegation, rcu);
358383e460STrond Myklebust 
36905f8d16STrond Myklebust 	nfs_do_free_delegation(delegation);
37905f8d16STrond Myklebust }
38905f8d16STrond Myklebust 
39905f8d16STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation)
40905f8d16STrond Myklebust {
41905f8d16STrond Myklebust 	call_rcu(&delegation->rcu, nfs_free_delegation_callback);
428383e460STrond Myklebust }
438383e460STrond Myklebust 
44b7391f44STrond Myklebust void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
45b7391f44STrond Myklebust {
46b7391f44STrond Myklebust 	set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
47b7391f44STrond Myklebust }
48b7391f44STrond Myklebust 
49bd7bf9d5STrond Myklebust int nfs_have_delegation(struct inode *inode, fmode_t flags)
50b7391f44STrond Myklebust {
51b7391f44STrond Myklebust 	struct nfs_delegation *delegation;
52b7391f44STrond Myklebust 	int ret = 0;
53b7391f44STrond Myklebust 
54b7391f44STrond Myklebust 	flags &= FMODE_READ|FMODE_WRITE;
55b7391f44STrond Myklebust 	rcu_read_lock();
56b7391f44STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
57b7391f44STrond Myklebust 	if (delegation != NULL && (delegation->type & flags) == flags) {
58b7391f44STrond Myklebust 		nfs_mark_delegation_referenced(delegation);
59b7391f44STrond Myklebust 		ret = 1;
60b7391f44STrond Myklebust 	}
61b7391f44STrond Myklebust 	rcu_read_unlock();
62b7391f44STrond Myklebust 	return ret;
63b7391f44STrond Myklebust }
64b7391f44STrond Myklebust 
65888e694cSTrond Myklebust static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
66888e694cSTrond Myklebust {
67888e694cSTrond Myklebust 	struct inode *inode = state->inode;
68888e694cSTrond Myklebust 	struct file_lock *fl;
69d5122201STrond Myklebust 	int status = 0;
70888e694cSTrond Myklebust 
713f09df70STrond Myklebust 	if (inode->i_flock == NULL)
723f09df70STrond Myklebust 		goto out;
733f09df70STrond Myklebust 
743f09df70STrond Myklebust 	/* Protect inode->i_flock using the BKL */
753f09df70STrond Myklebust 	lock_kernel();
7690dc7d27SHarvey Harrison 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
77888e694cSTrond Myklebust 		if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
78888e694cSTrond Myklebust 			continue;
79cd3758e3STrond Myklebust 		if (nfs_file_open_context(fl->fl_file) != ctx)
80888e694cSTrond Myklebust 			continue;
813f09df70STrond Myklebust 		unlock_kernel();
82888e694cSTrond Myklebust 		status = nfs4_lock_delegation_recall(state, fl);
83d5122201STrond Myklebust 		if (status < 0)
843f09df70STrond Myklebust 			goto out;
853f09df70STrond Myklebust 		lock_kernel();
86888e694cSTrond Myklebust 	}
873f09df70STrond Myklebust 	unlock_kernel();
883f09df70STrond Myklebust out:
89888e694cSTrond Myklebust 	return status;
90888e694cSTrond Myklebust }
91888e694cSTrond Myklebust 
92d18cc1fdSTrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
931da177e4SLinus Torvalds {
941da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
951da177e4SLinus Torvalds 	struct nfs_open_context *ctx;
961da177e4SLinus Torvalds 	struct nfs4_state *state;
97888e694cSTrond Myklebust 	int err;
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds again:
1001da177e4SLinus Torvalds 	spin_lock(&inode->i_lock);
1011da177e4SLinus Torvalds 	list_for_each_entry(ctx, &nfsi->open_files, list) {
1021da177e4SLinus Torvalds 		state = ctx->state;
1031da177e4SLinus Torvalds 		if (state == NULL)
1041da177e4SLinus Torvalds 			continue;
1051da177e4SLinus Torvalds 		if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
1061da177e4SLinus Torvalds 			continue;
10790163027STrond Myklebust 		if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0)
10890163027STrond Myklebust 			continue;
1091da177e4SLinus Torvalds 		get_nfs_open_context(ctx);
1101da177e4SLinus Torvalds 		spin_unlock(&inode->i_lock);
11113437e12STrond Myklebust 		err = nfs4_open_delegation_recall(ctx, state, stateid);
112888e694cSTrond Myklebust 		if (err >= 0)
113888e694cSTrond Myklebust 			err = nfs_delegation_claim_locks(ctx, state);
1141da177e4SLinus Torvalds 		put_nfs_open_context(ctx);
115888e694cSTrond Myklebust 		if (err != 0)
116d18cc1fdSTrond Myklebust 			return err;
1171da177e4SLinus Torvalds 		goto again;
1181da177e4SLinus Torvalds 	}
1191da177e4SLinus Torvalds 	spin_unlock(&inode->i_lock);
120d18cc1fdSTrond Myklebust 	return 0;
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds /*
1241da177e4SLinus Torvalds  * Set up a delegation on an inode
1251da177e4SLinus Torvalds  */
1261da177e4SLinus Torvalds void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
1271da177e4SLinus Torvalds {
1288f649c37STrond Myklebust 	struct nfs_delegation *delegation;
1298f649c37STrond Myklebust 	struct rpc_cred *oldcred = NULL;
1301da177e4SLinus Torvalds 
1318f649c37STrond Myklebust 	rcu_read_lock();
1328f649c37STrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
1338f649c37STrond Myklebust 	if (delegation != NULL) {
1348f649c37STrond Myklebust 		spin_lock(&delegation->lock);
1358f649c37STrond Myklebust 		if (delegation->inode != NULL) {
1361da177e4SLinus Torvalds 			memcpy(delegation->stateid.data, res->delegation.data,
1371da177e4SLinus Torvalds 			       sizeof(delegation->stateid.data));
1381da177e4SLinus Torvalds 			delegation->type = res->delegation_type;
1391da177e4SLinus Torvalds 			delegation->maxsize = res->maxsize;
14005c88babSTrond Myklebust 			oldcred = delegation->cred;
1411da177e4SLinus Torvalds 			delegation->cred = get_rpccred(cred);
1428f649c37STrond Myklebust 			clear_bit(NFS_DELEGATION_NEED_RECLAIM,
1438f649c37STrond Myklebust 				  &delegation->flags);
1441da177e4SLinus Torvalds 			NFS_I(inode)->delegation_state = delegation->type;
1458f649c37STrond Myklebust 			spin_unlock(&delegation->lock);
14605c88babSTrond Myklebust 			put_rpccred(oldcred);
1478f649c37STrond Myklebust 			rcu_read_unlock();
1488f649c37STrond Myklebust 		} else {
1498f649c37STrond Myklebust 			/* We appear to have raced with a delegation return. */
1508f649c37STrond Myklebust 			spin_unlock(&delegation->lock);
1518f649c37STrond Myklebust 			rcu_read_unlock();
1528f649c37STrond Myklebust 			nfs_inode_set_delegation(inode, cred, res);
1538f649c37STrond Myklebust 		}
1548f649c37STrond Myklebust 	} else {
1558f649c37STrond Myklebust 		rcu_read_unlock();
1568f649c37STrond Myklebust 	}
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds 
15957bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
16057bfa891STrond Myklebust {
16157bfa891STrond Myklebust 	int res = 0;
16257bfa891STrond Myklebust 
16357bfa891STrond Myklebust 	res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
16457bfa891STrond Myklebust 	nfs_free_delegation(delegation);
16557bfa891STrond Myklebust 	return res;
16657bfa891STrond Myklebust }
16757bfa891STrond Myklebust 
16886e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
16986e89489STrond Myklebust {
17086e89489STrond Myklebust 	struct inode *inode = NULL;
17186e89489STrond Myklebust 
17286e89489STrond Myklebust 	spin_lock(&delegation->lock);
17386e89489STrond Myklebust 	if (delegation->inode != NULL)
17486e89489STrond Myklebust 		inode = igrab(delegation->inode);
17586e89489STrond Myklebust 	spin_unlock(&delegation->lock);
17686e89489STrond Myklebust 	return inode;
17786e89489STrond Myklebust }
17886e89489STrond Myklebust 
17917d2c0a0SDavid Howells static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi,
18017d2c0a0SDavid Howells 							   const nfs4_stateid *stateid,
18117d2c0a0SDavid Howells 							   struct nfs_client *clp)
18257bfa891STrond Myklebust {
18317d2c0a0SDavid Howells 	struct nfs_delegation *delegation =
18417d2c0a0SDavid Howells 		rcu_dereference_protected(nfsi->delegation,
18517d2c0a0SDavid Howells 					  lockdep_is_held(&clp->cl_lock));
18657bfa891STrond Myklebust 
18757bfa891STrond Myklebust 	if (delegation == NULL)
18857bfa891STrond Myklebust 		goto nomatch;
18934310430STrond Myklebust 	spin_lock(&delegation->lock);
19057bfa891STrond Myklebust 	if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
19157bfa891STrond Myklebust 				sizeof(delegation->stateid.data)) != 0)
19234310430STrond Myklebust 		goto nomatch_unlock;
19357bfa891STrond Myklebust 	list_del_rcu(&delegation->super_list);
19486e89489STrond Myklebust 	delegation->inode = NULL;
19557bfa891STrond Myklebust 	nfsi->delegation_state = 0;
19657bfa891STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, NULL);
19734310430STrond Myklebust 	spin_unlock(&delegation->lock);
19857bfa891STrond Myklebust 	return delegation;
19934310430STrond Myklebust nomatch_unlock:
20034310430STrond Myklebust 	spin_unlock(&delegation->lock);
20157bfa891STrond Myklebust nomatch:
20257bfa891STrond Myklebust 	return NULL;
20357bfa891STrond Myklebust }
20457bfa891STrond Myklebust 
2051da177e4SLinus Torvalds /*
2061da177e4SLinus Torvalds  * Set up a delegation on an inode
2071da177e4SLinus Torvalds  */
2081da177e4SLinus Torvalds int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
2091da177e4SLinus Torvalds {
2107539bbabSDavid Howells 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
2111da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
21217d2c0a0SDavid Howells 	struct nfs_delegation *delegation, *old_delegation;
21357bfa891STrond Myklebust 	struct nfs_delegation *freeme = NULL;
2141da177e4SLinus Torvalds 	int status = 0;
2151da177e4SLinus Torvalds 
216f52720caSPanagiotis Issaris 	delegation = kmalloc(sizeof(*delegation), GFP_KERNEL);
2171da177e4SLinus Torvalds 	if (delegation == NULL)
2181da177e4SLinus Torvalds 		return -ENOMEM;
2191da177e4SLinus Torvalds 	memcpy(delegation->stateid.data, res->delegation.data,
2201da177e4SLinus Torvalds 			sizeof(delegation->stateid.data));
2211da177e4SLinus Torvalds 	delegation->type = res->delegation_type;
2221da177e4SLinus Torvalds 	delegation->maxsize = res->maxsize;
223beb2a5ecSTrond Myklebust 	delegation->change_attr = nfsi->change_attr;
2241da177e4SLinus Torvalds 	delegation->cred = get_rpccred(cred);
2251da177e4SLinus Torvalds 	delegation->inode = inode;
226b7391f44STrond Myklebust 	delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
22734310430STrond Myklebust 	spin_lock_init(&delegation->lock);
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
23017d2c0a0SDavid Howells 	old_delegation = rcu_dereference_protected(nfsi->delegation,
23117d2c0a0SDavid Howells 						   lockdep_is_held(&clp->cl_lock));
23217d2c0a0SDavid Howells 	if (old_delegation != NULL) {
23317d2c0a0SDavid Howells 		if (memcmp(&delegation->stateid, &old_delegation->stateid,
23417d2c0a0SDavid Howells 					sizeof(old_delegation->stateid)) == 0 &&
23517d2c0a0SDavid Howells 				delegation->type == old_delegation->type) {
23657bfa891STrond Myklebust 			goto out;
23757bfa891STrond Myklebust 		}
23857bfa891STrond Myklebust 		/*
23957bfa891STrond Myklebust 		 * Deal with broken servers that hand out two
24057bfa891STrond Myklebust 		 * delegations for the same file.
24157bfa891STrond Myklebust 		 */
24257bfa891STrond Myklebust 		dfprintk(FILE, "%s: server %s handed out "
24357bfa891STrond Myklebust 				"a duplicate delegation!\n",
2443110ff80SHarvey Harrison 				__func__, clp->cl_hostname);
24517d2c0a0SDavid Howells 		if (delegation->type <= old_delegation->type) {
24657bfa891STrond Myklebust 			freeme = delegation;
24757bfa891STrond Myklebust 			delegation = NULL;
24857bfa891STrond Myklebust 			goto out;
24957bfa891STrond Myklebust 		}
25017d2c0a0SDavid Howells 		freeme = nfs_detach_delegation_locked(nfsi, NULL, clp);
25157bfa891STrond Myklebust 	}
2528383e460STrond Myklebust 	list_add_rcu(&delegation->super_list, &clp->cl_delegations);
2531da177e4SLinus Torvalds 	nfsi->delegation_state = delegation->type;
2548383e460STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, delegation);
2551da177e4SLinus Torvalds 	delegation = NULL;
256412c77ceSTrond Myklebust 
257412c77ceSTrond Myklebust 	/* Ensure we revalidate the attributes and page cache! */
258412c77ceSTrond Myklebust 	spin_lock(&inode->i_lock);
259412c77ceSTrond Myklebust 	nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
260412c77ceSTrond Myklebust 	spin_unlock(&inode->i_lock);
261412c77ceSTrond Myklebust 
26257bfa891STrond Myklebust out:
2631da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
264603c83daSTrond Myklebust 	if (delegation != NULL)
265603c83daSTrond Myklebust 		nfs_free_delegation(delegation);
26657bfa891STrond Myklebust 	if (freeme != NULL)
26757bfa891STrond Myklebust 		nfs_do_return_delegation(inode, freeme, 0);
2681da177e4SLinus Torvalds 	return status;
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds /* Sync all data to disk upon delegation return */
2721da177e4SLinus Torvalds static void nfs_msync_inode(struct inode *inode)
2731da177e4SLinus Torvalds {
2741da177e4SLinus Torvalds 	filemap_fdatawrite(inode->i_mapping);
2751da177e4SLinus Torvalds 	nfs_wb_all(inode);
2761da177e4SLinus Torvalds 	filemap_fdatawait(inode->i_mapping);
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds /*
2801da177e4SLinus Torvalds  * Basic procedure for returning a delegation to the server
2811da177e4SLinus Torvalds  */
282d18cc1fdSTrond Myklebust static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
2831da177e4SLinus Torvalds {
2841da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
285d18cc1fdSTrond Myklebust 	int err;
2861da177e4SLinus Torvalds 
2873f09df70STrond Myklebust 	/*
2883f09df70STrond Myklebust 	 * Guard against new delegated open/lock/unlock calls and against
2893f09df70STrond Myklebust 	 * state recovery
2903f09df70STrond Myklebust 	 */
2911da177e4SLinus Torvalds 	down_write(&nfsi->rwsem);
292d18cc1fdSTrond Myklebust 	err = nfs_delegation_claim_opens(inode, &delegation->stateid);
2931da177e4SLinus Torvalds 	up_write(&nfsi->rwsem);
294d18cc1fdSTrond Myklebust 	if (err)
295d18cc1fdSTrond Myklebust 		goto out;
2961da177e4SLinus Torvalds 
297d18cc1fdSTrond Myklebust 	err = nfs_do_return_delegation(inode, delegation, issync);
298d18cc1fdSTrond Myklebust out:
299d18cc1fdSTrond Myklebust 	return err;
30090163027STrond Myklebust }
30190163027STrond Myklebust 
302e6f81075STrond Myklebust /*
303515d8611STrond Myklebust  * Return all delegations that have been marked for return
304515d8611STrond Myklebust  */
305d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp)
306515d8611STrond Myklebust {
307515d8611STrond Myklebust 	struct nfs_delegation *delegation;
308515d8611STrond Myklebust 	struct inode *inode;
309d18cc1fdSTrond Myklebust 	int err = 0;
310515d8611STrond Myklebust 
311515d8611STrond Myklebust restart:
312515d8611STrond Myklebust 	rcu_read_lock();
313515d8611STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
314515d8611STrond Myklebust 		if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
315515d8611STrond Myklebust 			continue;
316515d8611STrond Myklebust 		inode = nfs_delegation_grab_inode(delegation);
317515d8611STrond Myklebust 		if (inode == NULL)
318515d8611STrond Myklebust 			continue;
319515d8611STrond Myklebust 		spin_lock(&clp->cl_lock);
32017d2c0a0SDavid Howells 		delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp);
321515d8611STrond Myklebust 		spin_unlock(&clp->cl_lock);
322515d8611STrond Myklebust 		rcu_read_unlock();
323d18cc1fdSTrond Myklebust 		if (delegation != NULL) {
324d18cc1fdSTrond Myklebust 			filemap_flush(inode->i_mapping);
325d18cc1fdSTrond Myklebust 			err = __nfs_inode_return_delegation(inode, delegation, 0);
326d18cc1fdSTrond Myklebust 		}
327515d8611STrond Myklebust 		iput(inode);
328d18cc1fdSTrond Myklebust 		if (!err)
329515d8611STrond Myklebust 			goto restart;
330d18cc1fdSTrond Myklebust 		set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
331d18cc1fdSTrond Myklebust 		return err;
332515d8611STrond Myklebust 	}
333515d8611STrond Myklebust 	rcu_read_unlock();
334d18cc1fdSTrond Myklebust 	return 0;
335515d8611STrond Myklebust }
336515d8611STrond Myklebust 
337515d8611STrond Myklebust /*
338e6f81075STrond Myklebust  * This function returns the delegation without reclaiming opens
339e6f81075STrond Myklebust  * or protecting against delegation reclaims.
340e6f81075STrond Myklebust  * It is therefore really only safe to be called from
341e6f81075STrond Myklebust  * nfs4_clear_inode()
342e6f81075STrond Myklebust  */
343e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode)
344e6f81075STrond Myklebust {
345e6f81075STrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
346e6f81075STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
347e6f81075STrond Myklebust 	struct nfs_delegation *delegation;
348e6f81075STrond Myklebust 
34917d2c0a0SDavid Howells 	if (rcu_access_pointer(nfsi->delegation) != NULL) {
350e6f81075STrond Myklebust 		spin_lock(&clp->cl_lock);
35117d2c0a0SDavid Howells 		delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);
352e6f81075STrond Myklebust 		spin_unlock(&clp->cl_lock);
353e6f81075STrond Myklebust 		if (delegation != NULL)
354e6f81075STrond Myklebust 			nfs_do_return_delegation(inode, delegation, 0);
355e6f81075STrond Myklebust 	}
356e6f81075STrond Myklebust }
357e6f81075STrond Myklebust 
35890163027STrond Myklebust int nfs_inode_return_delegation(struct inode *inode)
35990163027STrond Myklebust {
36090163027STrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
36190163027STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
36290163027STrond Myklebust 	struct nfs_delegation *delegation;
36390163027STrond Myklebust 	int err = 0;
36490163027STrond Myklebust 
36517d2c0a0SDavid Howells 	if (rcu_access_pointer(nfsi->delegation) != NULL) {
36690163027STrond Myklebust 		spin_lock(&clp->cl_lock);
36717d2c0a0SDavid Howells 		delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);
36890163027STrond Myklebust 		spin_unlock(&clp->cl_lock);
369d18cc1fdSTrond Myklebust 		if (delegation != NULL) {
370d18cc1fdSTrond Myklebust 			nfs_msync_inode(inode);
371d18cc1fdSTrond Myklebust 			err = __nfs_inode_return_delegation(inode, delegation, 1);
372d18cc1fdSTrond Myklebust 		}
37390163027STrond Myklebust 	}
37490163027STrond Myklebust 	return err;
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds 
3776411bd4aSTrond Myklebust static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation)
3786411bd4aSTrond Myklebust {
3796411bd4aSTrond Myklebust 	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
3806411bd4aSTrond Myklebust 	set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
3816411bd4aSTrond Myklebust }
3826411bd4aSTrond Myklebust 
3831da177e4SLinus Torvalds /*
3841da177e4SLinus Torvalds  * Return all delegations associated to a super block
3851da177e4SLinus Torvalds  */
386515d8611STrond Myklebust void nfs_super_return_all_delegations(struct super_block *sb)
3871da177e4SLinus Torvalds {
3887539bbabSDavid Howells 	struct nfs_client *clp = NFS_SB(sb)->nfs_client;
3891da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 	if (clp == NULL)
3921da177e4SLinus Torvalds 		return;
3938383e460STrond Myklebust 	rcu_read_lock();
3948383e460STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
39586e89489STrond Myklebust 		spin_lock(&delegation->lock);
39686e89489STrond Myklebust 		if (delegation->inode != NULL && delegation->inode->i_sb == sb)
397515d8611STrond Myklebust 			set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
39886e89489STrond Myklebust 		spin_unlock(&delegation->lock);
3991da177e4SLinus Torvalds 	}
4008383e460STrond Myklebust 	rcu_read_unlock();
401d18cc1fdSTrond Myklebust 	if (nfs_client_return_marked_delegations(clp) != 0)
402d18cc1fdSTrond Myklebust 		nfs4_schedule_state_manager(clp);
403515d8611STrond Myklebust }
404515d8611STrond Myklebust 
405c79571a5SAlexandros Batsakis static
406c79571a5SAlexandros Batsakis void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, fmode_t flags)
407515d8611STrond Myklebust {
408515d8611STrond Myklebust 	struct nfs_delegation *delegation;
409515d8611STrond Myklebust 
410515d8611STrond Myklebust 	rcu_read_lock();
411707fb4b3STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
412c79571a5SAlexandros Batsakis 		if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
413c79571a5SAlexandros Batsakis 			continue;
414c79571a5SAlexandros Batsakis 		if (delegation->type & flags)
415b4a6f496SAlexandros Batsakis 			nfs_mark_return_delegation(clp, delegation);
416707fb4b3STrond Myklebust 	}
417515d8611STrond Myklebust 	rcu_read_unlock();
4181da177e4SLinus Torvalds }
4191da177e4SLinus Torvalds 
420c79571a5SAlexandros Batsakis static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
421c79571a5SAlexandros Batsakis {
422c79571a5SAlexandros Batsakis 	nfs_client_mark_return_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
423c79571a5SAlexandros Batsakis }
424c79571a5SAlexandros Batsakis 
425b0d3ded1STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp)
42658d9714aSTrond Myklebust {
427b0d3ded1STrond Myklebust 	if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
428b0d3ded1STrond Myklebust 		nfs4_schedule_state_manager(clp);
42958d9714aSTrond Myklebust }
43058d9714aSTrond Myklebust 
43131f09607SAlexandros Batsakis void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags)
432c79571a5SAlexandros Batsakis {
433c79571a5SAlexandros Batsakis 	nfs_client_mark_return_all_delegation_types(clp, flags);
434c79571a5SAlexandros Batsakis 	nfs_delegation_run_state_manager(clp);
435c79571a5SAlexandros Batsakis }
436c79571a5SAlexandros Batsakis 
437adfa6f98SDavid Howells void nfs_expire_all_delegations(struct nfs_client *clp)
43858d9714aSTrond Myklebust {
439c79571a5SAlexandros Batsakis 	nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
44058d9714aSTrond Myklebust }
44158d9714aSTrond Myklebust 
4421da177e4SLinus Torvalds /*
4431da177e4SLinus Torvalds  * Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
4441da177e4SLinus Torvalds  */
445adfa6f98SDavid Howells void nfs_handle_cb_pathdown(struct nfs_client *clp)
4461da177e4SLinus Torvalds {
4471da177e4SLinus Torvalds 	if (clp == NULL)
4481da177e4SLinus Torvalds 		return;
449707fb4b3STrond Myklebust 	nfs_client_mark_return_all_delegations(clp);
4501da177e4SLinus Torvalds }
4511da177e4SLinus Torvalds 
452b7391f44STrond Myklebust static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp)
453b7391f44STrond Myklebust {
454b7391f44STrond Myklebust 	struct nfs_delegation *delegation;
455b7391f44STrond Myklebust 
456b7391f44STrond Myklebust 	rcu_read_lock();
457b7391f44STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
458b7391f44STrond Myklebust 		if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
459b7391f44STrond Myklebust 			continue;
460b4a6f496SAlexandros Batsakis 		nfs_mark_return_delegation(clp, delegation);
461b7391f44STrond Myklebust 	}
462b7391f44STrond Myklebust 	rcu_read_unlock();
463b7391f44STrond Myklebust }
464b7391f44STrond Myklebust 
465b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
466b7391f44STrond Myklebust {
467b7391f44STrond Myklebust 	nfs_client_mark_return_unreferenced_delegations(clp);
468b7391f44STrond Myklebust 	nfs_delegation_run_state_manager(clp);
469b7391f44STrond Myklebust }
470b7391f44STrond Myklebust 
4711da177e4SLinus Torvalds /*
4721da177e4SLinus Torvalds  * Asynchronous delegation recall!
4731da177e4SLinus Torvalds  */
4742597641dSAlexandros Batsakis int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid,
4752597641dSAlexandros Batsakis 				      int (*validate_stateid)(struct nfs_delegation *delegation,
4762597641dSAlexandros Batsakis 							      const nfs4_stateid *stateid))
4771da177e4SLinus Torvalds {
4786411bd4aSTrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
4796411bd4aSTrond Myklebust 	struct nfs_delegation *delegation;
4801da177e4SLinus Torvalds 
4816411bd4aSTrond Myklebust 	rcu_read_lock();
4826411bd4aSTrond Myklebust 	delegation = rcu_dereference(NFS_I(inode)->delegation);
4832597641dSAlexandros Batsakis 
4842597641dSAlexandros Batsakis 	if (!validate_stateid(delegation, stateid)) {
4856411bd4aSTrond Myklebust 		rcu_read_unlock();
4866411bd4aSTrond Myklebust 		return -ENOENT;
4876411bd4aSTrond Myklebust 	}
4882597641dSAlexandros Batsakis 
4896411bd4aSTrond Myklebust 	nfs_mark_return_delegation(clp, delegation);
4906411bd4aSTrond Myklebust 	rcu_read_unlock();
4916411bd4aSTrond Myklebust 	nfs_delegation_run_state_manager(clp);
4926411bd4aSTrond Myklebust 	return 0;
4931da177e4SLinus Torvalds }
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds /*
4961da177e4SLinus Torvalds  * Retrieve the inode associated with a delegation
4971da177e4SLinus Torvalds  */
498adfa6f98SDavid Howells struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle)
4991da177e4SLinus Torvalds {
5001da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
5011da177e4SLinus Torvalds 	struct inode *res = NULL;
5028383e460STrond Myklebust 	rcu_read_lock();
5038383e460STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
50486e89489STrond Myklebust 		spin_lock(&delegation->lock);
50586e89489STrond Myklebust 		if (delegation->inode != NULL &&
50686e89489STrond Myklebust 		    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
5071da177e4SLinus Torvalds 			res = igrab(delegation->inode);
5081da177e4SLinus Torvalds 		}
50986e89489STrond Myklebust 		spin_unlock(&delegation->lock);
51086e89489STrond Myklebust 		if (res != NULL)
51186e89489STrond Myklebust 			break;
5121da177e4SLinus Torvalds 	}
5138383e460STrond Myklebust 	rcu_read_unlock();
5141da177e4SLinus Torvalds 	return res;
5151da177e4SLinus Torvalds }
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds /*
5181da177e4SLinus Torvalds  * Mark all delegations as needing to be reclaimed
5191da177e4SLinus Torvalds  */
520adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp)
5211da177e4SLinus Torvalds {
5221da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
5238383e460STrond Myklebust 	rcu_read_lock();
5248383e460STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list)
52515c831bfSTrond Myklebust 		set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
5268383e460STrond Myklebust 	rcu_read_unlock();
5271da177e4SLinus Torvalds }
5281da177e4SLinus Torvalds 
5291da177e4SLinus Torvalds /*
5301da177e4SLinus Torvalds  * Reap all unclaimed delegations after reboot recovery is done
5311da177e4SLinus Torvalds  */
532adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
5331da177e4SLinus Torvalds {
5348383e460STrond Myklebust 	struct nfs_delegation *delegation;
53586e89489STrond Myklebust 	struct inode *inode;
5368383e460STrond Myklebust restart:
5378383e460STrond Myklebust 	rcu_read_lock();
5388383e460STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
53915c831bfSTrond Myklebust 		if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0)
5401da177e4SLinus Torvalds 			continue;
54186e89489STrond Myklebust 		inode = nfs_delegation_grab_inode(delegation);
54286e89489STrond Myklebust 		if (inode == NULL)
54386e89489STrond Myklebust 			continue;
5448383e460STrond Myklebust 		spin_lock(&clp->cl_lock);
54517d2c0a0SDavid Howells 		delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp);
5461da177e4SLinus Torvalds 		spin_unlock(&clp->cl_lock);
5478383e460STrond Myklebust 		rcu_read_unlock();
5488383e460STrond Myklebust 		if (delegation != NULL)
549905f8d16STrond Myklebust 			nfs_free_delegation(delegation);
55086e89489STrond Myklebust 		iput(inode);
5518383e460STrond Myklebust 		goto restart;
5521da177e4SLinus Torvalds 	}
5538383e460STrond Myklebust 	rcu_read_unlock();
5541da177e4SLinus Torvalds }
5553e4f6290STrond Myklebust 
5563e4f6290STrond Myklebust int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
5573e4f6290STrond Myklebust {
5583e4f6290STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
5593e4f6290STrond Myklebust 	struct nfs_delegation *delegation;
5608383e460STrond Myklebust 	int ret = 0;
5613e4f6290STrond Myklebust 
5628383e460STrond Myklebust 	rcu_read_lock();
5638383e460STrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
5643e4f6290STrond Myklebust 	if (delegation != NULL) {
5653e4f6290STrond Myklebust 		memcpy(dst->data, delegation->stateid.data, sizeof(dst->data));
5668383e460STrond Myklebust 		ret = 1;
5673e4f6290STrond Myklebust 	}
5688383e460STrond Myklebust 	rcu_read_unlock();
5698383e460STrond Myklebust 	return ret;
5703e4f6290STrond Myklebust }
571