xref: /openbmc/linux/fs/nfs/delegation.c (revision 90dc7d27)
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>
131da177e4SLinus Torvalds #include <linux/spinlock.h>
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds #include <linux/nfs4.h>
161da177e4SLinus Torvalds #include <linux/nfs_fs.h>
171da177e4SLinus Torvalds #include <linux/nfs_xdr.h>
181da177e4SLinus Torvalds 
194ce79717STrond Myklebust #include "nfs4_fs.h"
201da177e4SLinus Torvalds #include "delegation.h"
2124c8dbbbSDavid Howells #include "internal.h"
221da177e4SLinus Torvalds 
23905f8d16STrond Myklebust static void nfs_do_free_delegation(struct nfs_delegation *delegation)
241da177e4SLinus Torvalds {
251da177e4SLinus Torvalds 	kfree(delegation);
261da177e4SLinus Torvalds }
271da177e4SLinus Torvalds 
288383e460STrond Myklebust static void nfs_free_delegation_callback(struct rcu_head *head)
298383e460STrond Myklebust {
308383e460STrond Myklebust 	struct nfs_delegation *delegation = container_of(head, struct nfs_delegation, rcu);
318383e460STrond Myklebust 
32905f8d16STrond Myklebust 	nfs_do_free_delegation(delegation);
33905f8d16STrond Myklebust }
34905f8d16STrond Myklebust 
35905f8d16STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation)
36905f8d16STrond Myklebust {
37905f8d16STrond Myklebust 	struct rpc_cred *cred;
38905f8d16STrond Myklebust 
39905f8d16STrond Myklebust 	cred = rcu_dereference(delegation->cred);
40905f8d16STrond Myklebust 	rcu_assign_pointer(delegation->cred, NULL);
41905f8d16STrond Myklebust 	call_rcu(&delegation->rcu, nfs_free_delegation_callback);
42905f8d16STrond Myklebust 	if (cred)
43905f8d16STrond Myklebust 		put_rpccred(cred);
448383e460STrond Myklebust }
458383e460STrond Myklebust 
46888e694cSTrond Myklebust static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
47888e694cSTrond Myklebust {
48888e694cSTrond Myklebust 	struct inode *inode = state->inode;
49888e694cSTrond Myklebust 	struct file_lock *fl;
50888e694cSTrond Myklebust 	int status;
51888e694cSTrond Myklebust 
5290dc7d27SHarvey Harrison 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
53888e694cSTrond Myklebust 		if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
54888e694cSTrond Myklebust 			continue;
55cd3758e3STrond Myklebust 		if (nfs_file_open_context(fl->fl_file) != ctx)
56888e694cSTrond Myklebust 			continue;
57888e694cSTrond Myklebust 		status = nfs4_lock_delegation_recall(state, fl);
58888e694cSTrond Myklebust 		if (status >= 0)
59888e694cSTrond Myklebust 			continue;
60888e694cSTrond Myklebust 		switch (status) {
61888e694cSTrond Myklebust 			default:
62888e694cSTrond Myklebust 				printk(KERN_ERR "%s: unhandled error %d.\n",
63888e694cSTrond Myklebust 						__FUNCTION__, status);
64888e694cSTrond Myklebust 			case -NFS4ERR_EXPIRED:
65888e694cSTrond Myklebust 				/* kill_proc(fl->fl_pid, SIGLOST, 1); */
66888e694cSTrond Myklebust 			case -NFS4ERR_STALE_CLIENTID:
677539bbabSDavid Howells 				nfs4_schedule_state_recovery(NFS_SERVER(inode)->nfs_client);
68888e694cSTrond Myklebust 				goto out_err;
69888e694cSTrond Myklebust 		}
70888e694cSTrond Myklebust 	}
71888e694cSTrond Myklebust 	return 0;
72888e694cSTrond Myklebust out_err:
73888e694cSTrond Myklebust 	return status;
74888e694cSTrond Myklebust }
75888e694cSTrond Myklebust 
7690163027STrond Myklebust static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
771da177e4SLinus Torvalds {
781da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
791da177e4SLinus Torvalds 	struct nfs_open_context *ctx;
801da177e4SLinus Torvalds 	struct nfs4_state *state;
81888e694cSTrond Myklebust 	int err;
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds again:
841da177e4SLinus Torvalds 	spin_lock(&inode->i_lock);
851da177e4SLinus Torvalds 	list_for_each_entry(ctx, &nfsi->open_files, list) {
861da177e4SLinus Torvalds 		state = ctx->state;
871da177e4SLinus Torvalds 		if (state == NULL)
881da177e4SLinus Torvalds 			continue;
891da177e4SLinus Torvalds 		if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
901da177e4SLinus Torvalds 			continue;
9190163027STrond Myklebust 		if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0)
9290163027STrond Myklebust 			continue;
931da177e4SLinus Torvalds 		get_nfs_open_context(ctx);
941da177e4SLinus Torvalds 		spin_unlock(&inode->i_lock);
9513437e12STrond Myklebust 		err = nfs4_open_delegation_recall(ctx, state, stateid);
96888e694cSTrond Myklebust 		if (err >= 0)
97888e694cSTrond Myklebust 			err = nfs_delegation_claim_locks(ctx, state);
981da177e4SLinus Torvalds 		put_nfs_open_context(ctx);
99888e694cSTrond Myklebust 		if (err != 0)
100888e694cSTrond Myklebust 			return;
1011da177e4SLinus Torvalds 		goto again;
1021da177e4SLinus Torvalds 	}
1031da177e4SLinus Torvalds 	spin_unlock(&inode->i_lock);
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds /*
1071da177e4SLinus Torvalds  * Set up a delegation on an inode
1081da177e4SLinus Torvalds  */
1091da177e4SLinus Torvalds void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds 	struct nfs_delegation *delegation = NFS_I(inode)->delegation;
11205c88babSTrond Myklebust 	struct rpc_cred *oldcred;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	if (delegation == NULL)
1151da177e4SLinus Torvalds 		return;
1161da177e4SLinus Torvalds 	memcpy(delegation->stateid.data, res->delegation.data,
1171da177e4SLinus Torvalds 			sizeof(delegation->stateid.data));
1181da177e4SLinus Torvalds 	delegation->type = res->delegation_type;
1191da177e4SLinus Torvalds 	delegation->maxsize = res->maxsize;
12005c88babSTrond Myklebust 	oldcred = delegation->cred;
1211da177e4SLinus Torvalds 	delegation->cred = get_rpccred(cred);
1221da177e4SLinus Torvalds 	delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM;
1231da177e4SLinus Torvalds 	NFS_I(inode)->delegation_state = delegation->type;
1241da177e4SLinus Torvalds 	smp_wmb();
12505c88babSTrond Myklebust 	put_rpccred(oldcred);
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds 
12857bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
12957bfa891STrond Myklebust {
13057bfa891STrond Myklebust 	int res = 0;
13157bfa891STrond Myklebust 
13257bfa891STrond Myklebust 	res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
13357bfa891STrond Myklebust 	nfs_free_delegation(delegation);
13457bfa891STrond Myklebust 	return res;
13557bfa891STrond Myklebust }
13657bfa891STrond Myklebust 
13757bfa891STrond Myklebust static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid)
13857bfa891STrond Myklebust {
13957bfa891STrond Myklebust 	struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
14057bfa891STrond Myklebust 
14157bfa891STrond Myklebust 	if (delegation == NULL)
14257bfa891STrond Myklebust 		goto nomatch;
14357bfa891STrond Myklebust 	if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
14457bfa891STrond Myklebust 				sizeof(delegation->stateid.data)) != 0)
14557bfa891STrond Myklebust 		goto nomatch;
14657bfa891STrond Myklebust 	list_del_rcu(&delegation->super_list);
14757bfa891STrond Myklebust 	nfsi->delegation_state = 0;
14857bfa891STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, NULL);
14957bfa891STrond Myklebust 	return delegation;
15057bfa891STrond Myklebust nomatch:
15157bfa891STrond Myklebust 	return NULL;
15257bfa891STrond Myklebust }
15357bfa891STrond Myklebust 
1541da177e4SLinus Torvalds /*
1551da177e4SLinus Torvalds  * Set up a delegation on an inode
1561da177e4SLinus Torvalds  */
1571da177e4SLinus Torvalds int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
1581da177e4SLinus Torvalds {
1597539bbabSDavid Howells 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
1601da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
1611da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
16257bfa891STrond Myklebust 	struct nfs_delegation *freeme = NULL;
1631da177e4SLinus Torvalds 	int status = 0;
1641da177e4SLinus Torvalds 
165f52720caSPanagiotis Issaris 	delegation = kmalloc(sizeof(*delegation), GFP_KERNEL);
1661da177e4SLinus Torvalds 	if (delegation == NULL)
1671da177e4SLinus Torvalds 		return -ENOMEM;
1681da177e4SLinus Torvalds 	memcpy(delegation->stateid.data, res->delegation.data,
1691da177e4SLinus Torvalds 			sizeof(delegation->stateid.data));
1701da177e4SLinus Torvalds 	delegation->type = res->delegation_type;
1711da177e4SLinus Torvalds 	delegation->maxsize = res->maxsize;
172beb2a5ecSTrond Myklebust 	delegation->change_attr = nfsi->change_attr;
1731da177e4SLinus Torvalds 	delegation->cred = get_rpccred(cred);
1741da177e4SLinus Torvalds 	delegation->inode = inode;
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
17757bfa891STrond Myklebust 	if (rcu_dereference(nfsi->delegation) != NULL) {
17857bfa891STrond Myklebust 		if (memcmp(&delegation->stateid, &nfsi->delegation->stateid,
17957bfa891STrond Myklebust 					sizeof(delegation->stateid)) == 0 &&
18057bfa891STrond Myklebust 				delegation->type == nfsi->delegation->type) {
18157bfa891STrond Myklebust 			goto out;
18257bfa891STrond Myklebust 		}
18357bfa891STrond Myklebust 		/*
18457bfa891STrond Myklebust 		 * Deal with broken servers that hand out two
18557bfa891STrond Myklebust 		 * delegations for the same file.
18657bfa891STrond Myklebust 		 */
18757bfa891STrond Myklebust 		dfprintk(FILE, "%s: server %s handed out "
18857bfa891STrond Myklebust 				"a duplicate delegation!\n",
18957bfa891STrond Myklebust 				__FUNCTION__, clp->cl_hostname);
19057bfa891STrond Myklebust 		if (delegation->type <= nfsi->delegation->type) {
19157bfa891STrond Myklebust 			freeme = delegation;
19257bfa891STrond Myklebust 			delegation = NULL;
19357bfa891STrond Myklebust 			goto out;
19457bfa891STrond Myklebust 		}
19557bfa891STrond Myklebust 		freeme = nfs_detach_delegation_locked(nfsi, NULL);
19657bfa891STrond Myklebust 	}
1978383e460STrond Myklebust 	list_add_rcu(&delegation->super_list, &clp->cl_delegations);
1981da177e4SLinus Torvalds 	nfsi->delegation_state = delegation->type;
1998383e460STrond Myklebust 	rcu_assign_pointer(nfsi->delegation, delegation);
2001da177e4SLinus Torvalds 	delegation = NULL;
201412c77ceSTrond Myklebust 
202412c77ceSTrond Myklebust 	/* Ensure we revalidate the attributes and page cache! */
203412c77ceSTrond Myklebust 	spin_lock(&inode->i_lock);
204412c77ceSTrond Myklebust 	nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
205412c77ceSTrond Myklebust 	spin_unlock(&inode->i_lock);
206412c77ceSTrond Myklebust 
20757bfa891STrond Myklebust out:
2081da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
209603c83daSTrond Myklebust 	if (delegation != NULL)
210603c83daSTrond Myklebust 		nfs_free_delegation(delegation);
21157bfa891STrond Myklebust 	if (freeme != NULL)
21257bfa891STrond Myklebust 		nfs_do_return_delegation(inode, freeme, 0);
2131da177e4SLinus Torvalds 	return status;
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds /* Sync all data to disk upon delegation return */
2171da177e4SLinus Torvalds static void nfs_msync_inode(struct inode *inode)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	filemap_fdatawrite(inode->i_mapping);
2201da177e4SLinus Torvalds 	nfs_wb_all(inode);
2211da177e4SLinus Torvalds 	filemap_fdatawait(inode->i_mapping);
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds /*
2251da177e4SLinus Torvalds  * Basic procedure for returning a delegation to the server
2261da177e4SLinus Torvalds  */
22790163027STrond Myklebust static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
2281da177e4SLinus Torvalds {
2297539bbabSDavid Howells 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
2301da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds 	nfs_msync_inode(inode);
2331da177e4SLinus Torvalds 	down_read(&clp->cl_sem);
2341da177e4SLinus Torvalds 	/* Guard against new delegated open calls */
2351da177e4SLinus Torvalds 	down_write(&nfsi->rwsem);
23690163027STrond Myklebust 	nfs_delegation_claim_opens(inode, &delegation->stateid);
2371da177e4SLinus Torvalds 	up_write(&nfsi->rwsem);
2381da177e4SLinus Torvalds 	up_read(&clp->cl_sem);
2391da177e4SLinus Torvalds 	nfs_msync_inode(inode);
2401da177e4SLinus Torvalds 
241e6f81075STrond Myklebust 	return nfs_do_return_delegation(inode, delegation, 1);
24290163027STrond Myklebust }
24390163027STrond Myklebust 
244e6f81075STrond Myklebust /*
245e6f81075STrond Myklebust  * This function returns the delegation without reclaiming opens
246e6f81075STrond Myklebust  * or protecting against delegation reclaims.
247e6f81075STrond Myklebust  * It is therefore really only safe to be called from
248e6f81075STrond Myklebust  * nfs4_clear_inode()
249e6f81075STrond Myklebust  */
250e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode)
251e6f81075STrond Myklebust {
252e6f81075STrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
253e6f81075STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
254e6f81075STrond Myklebust 	struct nfs_delegation *delegation;
255e6f81075STrond Myklebust 
256e6f81075STrond Myklebust 	if (rcu_dereference(nfsi->delegation) != NULL) {
257e6f81075STrond Myklebust 		spin_lock(&clp->cl_lock);
258e6f81075STrond Myklebust 		delegation = nfs_detach_delegation_locked(nfsi, NULL);
259e6f81075STrond Myklebust 		spin_unlock(&clp->cl_lock);
260e6f81075STrond Myklebust 		if (delegation != NULL)
261e6f81075STrond Myklebust 			nfs_do_return_delegation(inode, delegation, 0);
262e6f81075STrond Myklebust 	}
263e6f81075STrond Myklebust }
264e6f81075STrond Myklebust 
26590163027STrond Myklebust int nfs_inode_return_delegation(struct inode *inode)
26690163027STrond Myklebust {
26790163027STrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
26890163027STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
26990163027STrond Myklebust 	struct nfs_delegation *delegation;
27090163027STrond Myklebust 	int err = 0;
27190163027STrond Myklebust 
2728383e460STrond Myklebust 	if (rcu_dereference(nfsi->delegation) != NULL) {
27390163027STrond Myklebust 		spin_lock(&clp->cl_lock);
27490163027STrond Myklebust 		delegation = nfs_detach_delegation_locked(nfsi, NULL);
27590163027STrond Myklebust 		spin_unlock(&clp->cl_lock);
2761da177e4SLinus Torvalds 		if (delegation != NULL)
27790163027STrond Myklebust 			err = __nfs_inode_return_delegation(inode, delegation);
27890163027STrond Myklebust 	}
27990163027STrond Myklebust 	return err;
2801da177e4SLinus Torvalds }
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds /*
2831da177e4SLinus Torvalds  * Return all delegations associated to a super block
2841da177e4SLinus Torvalds  */
2851da177e4SLinus Torvalds void nfs_return_all_delegations(struct super_block *sb)
2861da177e4SLinus Torvalds {
2877539bbabSDavid Howells 	struct nfs_client *clp = NFS_SB(sb)->nfs_client;
2881da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
2891da177e4SLinus Torvalds 	struct inode *inode;
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds 	if (clp == NULL)
2921da177e4SLinus Torvalds 		return;
2931da177e4SLinus Torvalds restart:
2948383e460STrond Myklebust 	rcu_read_lock();
2958383e460STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
2961da177e4SLinus Torvalds 		if (delegation->inode->i_sb != sb)
2971da177e4SLinus Torvalds 			continue;
2981da177e4SLinus Torvalds 		inode = igrab(delegation->inode);
2991da177e4SLinus Torvalds 		if (inode == NULL)
3001da177e4SLinus Torvalds 			continue;
3018383e460STrond Myklebust 		spin_lock(&clp->cl_lock);
3028383e460STrond Myklebust 		delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
3031da177e4SLinus Torvalds 		spin_unlock(&clp->cl_lock);
3048383e460STrond Myklebust 		rcu_read_unlock();
3058383e460STrond Myklebust 		if (delegation != NULL)
30690163027STrond Myklebust 			__nfs_inode_return_delegation(inode, delegation);
3071da177e4SLinus Torvalds 		iput(inode);
3081da177e4SLinus Torvalds 		goto restart;
3091da177e4SLinus Torvalds 	}
3108383e460STrond Myklebust 	rcu_read_unlock();
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds 
31310afec90STrond Myklebust static int nfs_do_expire_all_delegations(void *ptr)
31458d9714aSTrond Myklebust {
315adfa6f98SDavid Howells 	struct nfs_client *clp = ptr;
31658d9714aSTrond Myklebust 	struct nfs_delegation *delegation;
31758d9714aSTrond Myklebust 	struct inode *inode;
31858d9714aSTrond Myklebust 
31958d9714aSTrond Myklebust 	allow_signal(SIGKILL);
32058d9714aSTrond Myklebust restart:
32158d9714aSTrond Myklebust 	if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0)
32258d9714aSTrond Myklebust 		goto out;
32358d9714aSTrond Myklebust 	if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0)
32458d9714aSTrond Myklebust 		goto out;
3258383e460STrond Myklebust 	rcu_read_lock();
3268383e460STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
32758d9714aSTrond Myklebust 		inode = igrab(delegation->inode);
32858d9714aSTrond Myklebust 		if (inode == NULL)
32958d9714aSTrond Myklebust 			continue;
3308383e460STrond Myklebust 		spin_lock(&clp->cl_lock);
3318383e460STrond Myklebust 		delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
33258d9714aSTrond Myklebust 		spin_unlock(&clp->cl_lock);
3338383e460STrond Myklebust 		rcu_read_unlock();
3348383e460STrond Myklebust 		if (delegation)
33590163027STrond Myklebust 			__nfs_inode_return_delegation(inode, delegation);
33658d9714aSTrond Myklebust 		iput(inode);
33758d9714aSTrond Myklebust 		goto restart;
33858d9714aSTrond Myklebust 	}
3398383e460STrond Myklebust 	rcu_read_unlock();
34058d9714aSTrond Myklebust out:
34124c8dbbbSDavid Howells 	nfs_put_client(clp);
34258d9714aSTrond Myklebust 	module_put_and_exit(0);
34358d9714aSTrond Myklebust }
34458d9714aSTrond Myklebust 
345adfa6f98SDavid Howells void nfs_expire_all_delegations(struct nfs_client *clp)
34658d9714aSTrond Myklebust {
34758d9714aSTrond Myklebust 	struct task_struct *task;
34858d9714aSTrond Myklebust 
34958d9714aSTrond Myklebust 	__module_get(THIS_MODULE);
35058d9714aSTrond Myklebust 	atomic_inc(&clp->cl_count);
35158d9714aSTrond Myklebust 	task = kthread_run(nfs_do_expire_all_delegations, clp,
3525d8515caSChuck Lever 				"%s-delegreturn",
3535d8515caSChuck Lever 				rpc_peeraddr2str(clp->cl_rpcclient,
3545d8515caSChuck Lever 							RPC_DISPLAY_ADDR));
35558d9714aSTrond Myklebust 	if (!IS_ERR(task))
35658d9714aSTrond Myklebust 		return;
35724c8dbbbSDavid Howells 	nfs_put_client(clp);
35858d9714aSTrond Myklebust 	module_put(THIS_MODULE);
35958d9714aSTrond Myklebust }
36058d9714aSTrond Myklebust 
3611da177e4SLinus Torvalds /*
3621da177e4SLinus Torvalds  * Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
3631da177e4SLinus Torvalds  */
364adfa6f98SDavid Howells void nfs_handle_cb_pathdown(struct nfs_client *clp)
3651da177e4SLinus Torvalds {
3661da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
3671da177e4SLinus Torvalds 	struct inode *inode;
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds 	if (clp == NULL)
3701da177e4SLinus Torvalds 		return;
3711da177e4SLinus Torvalds restart:
3728383e460STrond Myklebust 	rcu_read_lock();
3738383e460STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
3741da177e4SLinus Torvalds 		inode = igrab(delegation->inode);
3751da177e4SLinus Torvalds 		if (inode == NULL)
3761da177e4SLinus Torvalds 			continue;
3778383e460STrond Myklebust 		spin_lock(&clp->cl_lock);
3788383e460STrond Myklebust 		delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
3791da177e4SLinus Torvalds 		spin_unlock(&clp->cl_lock);
3808383e460STrond Myklebust 		rcu_read_unlock();
3818383e460STrond Myklebust 		if (delegation != NULL)
38290163027STrond Myklebust 			__nfs_inode_return_delegation(inode, delegation);
3831da177e4SLinus Torvalds 		iput(inode);
3841da177e4SLinus Torvalds 		goto restart;
3851da177e4SLinus Torvalds 	}
3868383e460STrond Myklebust 	rcu_read_unlock();
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds struct recall_threadargs {
3901da177e4SLinus Torvalds 	struct inode *inode;
391adfa6f98SDavid Howells 	struct nfs_client *clp;
3921da177e4SLinus Torvalds 	const nfs4_stateid *stateid;
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds 	struct completion started;
3951da177e4SLinus Torvalds 	int result;
3961da177e4SLinus Torvalds };
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds static int recall_thread(void *data)
3991da177e4SLinus Torvalds {
4001da177e4SLinus Torvalds 	struct recall_threadargs *args = (struct recall_threadargs *)data;
4011da177e4SLinus Torvalds 	struct inode *inode = igrab(args->inode);
4027539bbabSDavid Howells 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
4031da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
4041da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds 	daemonize("nfsv4-delegreturn");
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 	nfs_msync_inode(inode);
4091da177e4SLinus Torvalds 	down_read(&clp->cl_sem);
4101da177e4SLinus Torvalds 	down_write(&nfsi->rwsem);
4111da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
41290163027STrond Myklebust 	delegation = nfs_detach_delegation_locked(nfsi, args->stateid);
41390163027STrond Myklebust 	if (delegation != NULL)
4141da177e4SLinus Torvalds 		args->result = 0;
41590163027STrond Myklebust 	else
4161da177e4SLinus Torvalds 		args->result = -ENOENT;
4171da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
4181da177e4SLinus Torvalds 	complete(&args->started);
41990163027STrond Myklebust 	nfs_delegation_claim_opens(inode, args->stateid);
4201da177e4SLinus Torvalds 	up_write(&nfsi->rwsem);
4211da177e4SLinus Torvalds 	up_read(&clp->cl_sem);
4221da177e4SLinus Torvalds 	nfs_msync_inode(inode);
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds 	if (delegation != NULL)
425e6f81075STrond Myklebust 		nfs_do_return_delegation(inode, delegation, 1);
4261da177e4SLinus Torvalds 	iput(inode);
4271da177e4SLinus Torvalds 	module_put_and_exit(0);
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds /*
4311da177e4SLinus Torvalds  * Asynchronous delegation recall!
4321da177e4SLinus Torvalds  */
4331da177e4SLinus Torvalds int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
4341da177e4SLinus Torvalds {
4351da177e4SLinus Torvalds 	struct recall_threadargs data = {
4361da177e4SLinus Torvalds 		.inode = inode,
4371da177e4SLinus Torvalds 		.stateid = stateid,
4381da177e4SLinus Torvalds 	};
4391da177e4SLinus Torvalds 	int status;
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	init_completion(&data.started);
4421da177e4SLinus Torvalds 	__module_get(THIS_MODULE);
4431da177e4SLinus Torvalds 	status = kernel_thread(recall_thread, &data, CLONE_KERNEL);
4441da177e4SLinus Torvalds 	if (status < 0)
4451da177e4SLinus Torvalds 		goto out_module_put;
4461da177e4SLinus Torvalds 	wait_for_completion(&data.started);
4471da177e4SLinus Torvalds 	return data.result;
4481da177e4SLinus Torvalds out_module_put:
4491da177e4SLinus Torvalds 	module_put(THIS_MODULE);
4501da177e4SLinus Torvalds 	return status;
4511da177e4SLinus Torvalds }
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds /*
4541da177e4SLinus Torvalds  * Retrieve the inode associated with a delegation
4551da177e4SLinus Torvalds  */
456adfa6f98SDavid Howells struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle)
4571da177e4SLinus Torvalds {
4581da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
4591da177e4SLinus Torvalds 	struct inode *res = NULL;
4608383e460STrond Myklebust 	rcu_read_lock();
4618383e460STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
4621da177e4SLinus Torvalds 		if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
4631da177e4SLinus Torvalds 			res = igrab(delegation->inode);
4641da177e4SLinus Torvalds 			break;
4651da177e4SLinus Torvalds 		}
4661da177e4SLinus Torvalds 	}
4678383e460STrond Myklebust 	rcu_read_unlock();
4681da177e4SLinus Torvalds 	return res;
4691da177e4SLinus Torvalds }
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds /*
4721da177e4SLinus Torvalds  * Mark all delegations as needing to be reclaimed
4731da177e4SLinus Torvalds  */
474adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp)
4751da177e4SLinus Torvalds {
4761da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
4778383e460STrond Myklebust 	rcu_read_lock();
4788383e460STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list)
4791da177e4SLinus Torvalds 		delegation->flags |= NFS_DELEGATION_NEED_RECLAIM;
4808383e460STrond Myklebust 	rcu_read_unlock();
4811da177e4SLinus Torvalds }
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds /*
4841da177e4SLinus Torvalds  * Reap all unclaimed delegations after reboot recovery is done
4851da177e4SLinus Torvalds  */
486adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
4871da177e4SLinus Torvalds {
4888383e460STrond Myklebust 	struct nfs_delegation *delegation;
4898383e460STrond Myklebust restart:
4908383e460STrond Myklebust 	rcu_read_lock();
4918383e460STrond Myklebust 	list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
4921da177e4SLinus Torvalds 		if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0)
4931da177e4SLinus Torvalds 			continue;
4948383e460STrond Myklebust 		spin_lock(&clp->cl_lock);
4958383e460STrond Myklebust 		delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL);
4961da177e4SLinus Torvalds 		spin_unlock(&clp->cl_lock);
4978383e460STrond Myklebust 		rcu_read_unlock();
4988383e460STrond Myklebust 		if (delegation != NULL)
499905f8d16STrond Myklebust 			nfs_free_delegation(delegation);
5008383e460STrond Myklebust 		goto restart;
5011da177e4SLinus Torvalds 	}
5028383e460STrond Myklebust 	rcu_read_unlock();
5031da177e4SLinus Torvalds }
5043e4f6290STrond Myklebust 
5053e4f6290STrond Myklebust int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
5063e4f6290STrond Myklebust {
5073e4f6290STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
5083e4f6290STrond Myklebust 	struct nfs_delegation *delegation;
5098383e460STrond Myklebust 	int ret = 0;
5103e4f6290STrond Myklebust 
5118383e460STrond Myklebust 	rcu_read_lock();
5128383e460STrond Myklebust 	delegation = rcu_dereference(nfsi->delegation);
5133e4f6290STrond Myklebust 	if (delegation != NULL) {
5143e4f6290STrond Myklebust 		memcpy(dst->data, delegation->stateid.data, sizeof(dst->data));
5158383e460STrond Myklebust 		ret = 1;
5163e4f6290STrond Myklebust 	}
5178383e460STrond Myklebust 	rcu_read_unlock();
5188383e460STrond Myklebust 	return ret;
5193e4f6290STrond Myklebust }
520