xref: /openbmc/linux/fs/nfs/delegation.c (revision 4ce79717)
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/config.h>
101da177e4SLinus Torvalds #include <linux/completion.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"
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds static struct nfs_delegation *nfs_alloc_delegation(void)
231da177e4SLinus Torvalds {
241da177e4SLinus Torvalds 	return (struct nfs_delegation *)kmalloc(sizeof(struct nfs_delegation), GFP_KERNEL);
251da177e4SLinus Torvalds }
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds static void nfs_free_delegation(struct nfs_delegation *delegation)
281da177e4SLinus Torvalds {
291da177e4SLinus Torvalds 	if (delegation->cred)
301da177e4SLinus Torvalds 		put_rpccred(delegation->cred);
311da177e4SLinus Torvalds 	kfree(delegation);
321da177e4SLinus Torvalds }
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds static void nfs_delegation_claim_opens(struct inode *inode)
351da177e4SLinus Torvalds {
361da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
371da177e4SLinus Torvalds 	struct nfs_open_context *ctx;
381da177e4SLinus Torvalds 	struct nfs4_state *state;
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds again:
411da177e4SLinus Torvalds 	spin_lock(&inode->i_lock);
421da177e4SLinus Torvalds 	list_for_each_entry(ctx, &nfsi->open_files, list) {
431da177e4SLinus Torvalds 		state = ctx->state;
441da177e4SLinus Torvalds 		if (state == NULL)
451da177e4SLinus Torvalds 			continue;
461da177e4SLinus Torvalds 		if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
471da177e4SLinus Torvalds 			continue;
481da177e4SLinus Torvalds 		get_nfs_open_context(ctx);
491da177e4SLinus Torvalds 		spin_unlock(&inode->i_lock);
501da177e4SLinus Torvalds 		if (nfs4_open_delegation_recall(ctx->dentry, state) < 0)
511da177e4SLinus Torvalds 			return;
521da177e4SLinus Torvalds 		put_nfs_open_context(ctx);
531da177e4SLinus Torvalds 		goto again;
541da177e4SLinus Torvalds 	}
551da177e4SLinus Torvalds 	spin_unlock(&inode->i_lock);
561da177e4SLinus Torvalds }
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds /*
591da177e4SLinus Torvalds  * Set up a delegation on an inode
601da177e4SLinus Torvalds  */
611da177e4SLinus Torvalds void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds 	struct nfs_delegation *delegation = NFS_I(inode)->delegation;
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 	if (delegation == NULL)
661da177e4SLinus Torvalds 		return;
671da177e4SLinus Torvalds 	memcpy(delegation->stateid.data, res->delegation.data,
681da177e4SLinus Torvalds 			sizeof(delegation->stateid.data));
691da177e4SLinus Torvalds 	delegation->type = res->delegation_type;
701da177e4SLinus Torvalds 	delegation->maxsize = res->maxsize;
711da177e4SLinus Torvalds 	put_rpccred(cred);
721da177e4SLinus Torvalds 	delegation->cred = get_rpccred(cred);
731da177e4SLinus Torvalds 	delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM;
741da177e4SLinus Torvalds 	NFS_I(inode)->delegation_state = delegation->type;
751da177e4SLinus Torvalds 	smp_wmb();
761da177e4SLinus Torvalds }
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds /*
791da177e4SLinus Torvalds  * Set up a delegation on an inode
801da177e4SLinus Torvalds  */
811da177e4SLinus Torvalds int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
821da177e4SLinus Torvalds {
831da177e4SLinus Torvalds 	struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
841da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
851da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
861da177e4SLinus Torvalds 	int status = 0;
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	delegation = nfs_alloc_delegation();
891da177e4SLinus Torvalds 	if (delegation == NULL)
901da177e4SLinus Torvalds 		return -ENOMEM;
911da177e4SLinus Torvalds 	memcpy(delegation->stateid.data, res->delegation.data,
921da177e4SLinus Torvalds 			sizeof(delegation->stateid.data));
931da177e4SLinus Torvalds 	delegation->type = res->delegation_type;
941da177e4SLinus Torvalds 	delegation->maxsize = res->maxsize;
951da177e4SLinus Torvalds 	delegation->cred = get_rpccred(cred);
961da177e4SLinus Torvalds 	delegation->inode = inode;
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
991da177e4SLinus Torvalds 	if (nfsi->delegation == NULL) {
1001da177e4SLinus Torvalds 		list_add(&delegation->super_list, &clp->cl_delegations);
1011da177e4SLinus Torvalds 		nfsi->delegation = delegation;
1021da177e4SLinus Torvalds 		nfsi->delegation_state = delegation->type;
1031da177e4SLinus Torvalds 		delegation = NULL;
1041da177e4SLinus Torvalds 	} else {
1051da177e4SLinus Torvalds 		if (memcmp(&delegation->stateid, &nfsi->delegation->stateid,
1061da177e4SLinus Torvalds 					sizeof(delegation->stateid)) != 0 ||
1071da177e4SLinus Torvalds 				delegation->type != nfsi->delegation->type) {
1081da177e4SLinus Torvalds 			printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n",
1091da177e4SLinus Torvalds 					__FUNCTION__, NIPQUAD(clp->cl_addr));
1101da177e4SLinus Torvalds 			status = -EIO;
1111da177e4SLinus Torvalds 		}
1121da177e4SLinus Torvalds 	}
1131da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
1141da177e4SLinus Torvalds 	if (delegation != NULL)
1151da177e4SLinus Torvalds 		kfree(delegation);
1161da177e4SLinus Torvalds 	return status;
1171da177e4SLinus Torvalds }
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
1201da177e4SLinus Torvalds {
1211da177e4SLinus Torvalds 	int res = 0;
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 	__nfs_revalidate_inode(NFS_SERVER(inode), inode);
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 	res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid);
1261da177e4SLinus Torvalds 	nfs_free_delegation(delegation);
1271da177e4SLinus Torvalds 	return res;
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds /* Sync all data to disk upon delegation return */
1311da177e4SLinus Torvalds static void nfs_msync_inode(struct inode *inode)
1321da177e4SLinus Torvalds {
1331da177e4SLinus Torvalds 	filemap_fdatawrite(inode->i_mapping);
1341da177e4SLinus Torvalds 	nfs_wb_all(inode);
1351da177e4SLinus Torvalds 	filemap_fdatawait(inode->i_mapping);
1361da177e4SLinus Torvalds }
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds /*
1391da177e4SLinus Torvalds  * Basic procedure for returning a delegation to the server
1401da177e4SLinus Torvalds  */
1411da177e4SLinus Torvalds int nfs_inode_return_delegation(struct inode *inode)
1421da177e4SLinus Torvalds {
1431da177e4SLinus Torvalds 	struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
1441da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
1451da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
1461da177e4SLinus Torvalds 	int res = 0;
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds 	nfs_msync_inode(inode);
1491da177e4SLinus Torvalds 	down_read(&clp->cl_sem);
1501da177e4SLinus Torvalds 	/* Guard against new delegated open calls */
1511da177e4SLinus Torvalds 	down_write(&nfsi->rwsem);
1521da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
1531da177e4SLinus Torvalds 	delegation = nfsi->delegation;
1541da177e4SLinus Torvalds 	if (delegation != NULL) {
1551da177e4SLinus Torvalds 		list_del_init(&delegation->super_list);
1561da177e4SLinus Torvalds 		nfsi->delegation = NULL;
1571da177e4SLinus Torvalds 		nfsi->delegation_state = 0;
1581da177e4SLinus Torvalds 	}
1591da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
1601da177e4SLinus Torvalds 	nfs_delegation_claim_opens(inode);
1611da177e4SLinus Torvalds 	up_write(&nfsi->rwsem);
1621da177e4SLinus Torvalds 	up_read(&clp->cl_sem);
1631da177e4SLinus Torvalds 	nfs_msync_inode(inode);
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	if (delegation != NULL)
1661da177e4SLinus Torvalds 		res = nfs_do_return_delegation(inode, delegation);
1671da177e4SLinus Torvalds 	return res;
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds /*
1711da177e4SLinus Torvalds  * Return all delegations associated to a super block
1721da177e4SLinus Torvalds  */
1731da177e4SLinus Torvalds void nfs_return_all_delegations(struct super_block *sb)
1741da177e4SLinus Torvalds {
1751da177e4SLinus Torvalds 	struct nfs4_client *clp = NFS_SB(sb)->nfs4_state;
1761da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
1771da177e4SLinus Torvalds 	struct inode *inode;
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	if (clp == NULL)
1801da177e4SLinus Torvalds 		return;
1811da177e4SLinus Torvalds restart:
1821da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
1831da177e4SLinus Torvalds 	list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
1841da177e4SLinus Torvalds 		if (delegation->inode->i_sb != sb)
1851da177e4SLinus Torvalds 			continue;
1861da177e4SLinus Torvalds 		inode = igrab(delegation->inode);
1871da177e4SLinus Torvalds 		if (inode == NULL)
1881da177e4SLinus Torvalds 			continue;
1891da177e4SLinus Torvalds 		spin_unlock(&clp->cl_lock);
1901da177e4SLinus Torvalds 		nfs_inode_return_delegation(inode);
1911da177e4SLinus Torvalds 		iput(inode);
1921da177e4SLinus Torvalds 		goto restart;
1931da177e4SLinus Torvalds 	}
1941da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds /*
1981da177e4SLinus Torvalds  * Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
1991da177e4SLinus Torvalds  */
2001da177e4SLinus Torvalds void nfs_handle_cb_pathdown(struct nfs4_client *clp)
2011da177e4SLinus Torvalds {
2021da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
2031da177e4SLinus Torvalds 	struct inode *inode;
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	if (clp == NULL)
2061da177e4SLinus Torvalds 		return;
2071da177e4SLinus Torvalds restart:
2081da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
2091da177e4SLinus Torvalds 	list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
2101da177e4SLinus Torvalds 		inode = igrab(delegation->inode);
2111da177e4SLinus Torvalds 		if (inode == NULL)
2121da177e4SLinus Torvalds 			continue;
2131da177e4SLinus Torvalds 		spin_unlock(&clp->cl_lock);
2141da177e4SLinus Torvalds 		nfs_inode_return_delegation(inode);
2151da177e4SLinus Torvalds 		iput(inode);
2161da177e4SLinus Torvalds 		goto restart;
2171da177e4SLinus Torvalds 	}
2181da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds struct recall_threadargs {
2221da177e4SLinus Torvalds 	struct inode *inode;
2231da177e4SLinus Torvalds 	struct nfs4_client *clp;
2241da177e4SLinus Torvalds 	const nfs4_stateid *stateid;
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 	struct completion started;
2271da177e4SLinus Torvalds 	int result;
2281da177e4SLinus Torvalds };
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds static int recall_thread(void *data)
2311da177e4SLinus Torvalds {
2321da177e4SLinus Torvalds 	struct recall_threadargs *args = (struct recall_threadargs *)data;
2331da177e4SLinus Torvalds 	struct inode *inode = igrab(args->inode);
2341da177e4SLinus Torvalds 	struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
2351da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
2361da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds 	daemonize("nfsv4-delegreturn");
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	nfs_msync_inode(inode);
2411da177e4SLinus Torvalds 	down_read(&clp->cl_sem);
2421da177e4SLinus Torvalds 	down_write(&nfsi->rwsem);
2431da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
2441da177e4SLinus Torvalds 	delegation = nfsi->delegation;
2451da177e4SLinus Torvalds 	if (delegation != NULL && memcmp(delegation->stateid.data,
2461da177e4SLinus Torvalds 				args->stateid->data,
2471da177e4SLinus Torvalds 				sizeof(delegation->stateid.data)) == 0) {
2481da177e4SLinus Torvalds 		list_del_init(&delegation->super_list);
2491da177e4SLinus Torvalds 		nfsi->delegation = NULL;
2501da177e4SLinus Torvalds 		nfsi->delegation_state = 0;
2511da177e4SLinus Torvalds 		args->result = 0;
2521da177e4SLinus Torvalds 	} else {
2531da177e4SLinus Torvalds 		delegation = NULL;
2541da177e4SLinus Torvalds 		args->result = -ENOENT;
2551da177e4SLinus Torvalds 	}
2561da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
2571da177e4SLinus Torvalds 	complete(&args->started);
2581da177e4SLinus Torvalds 	nfs_delegation_claim_opens(inode);
2591da177e4SLinus Torvalds 	up_write(&nfsi->rwsem);
2601da177e4SLinus Torvalds 	up_read(&clp->cl_sem);
2611da177e4SLinus Torvalds 	nfs_msync_inode(inode);
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	if (delegation != NULL)
2641da177e4SLinus Torvalds 		nfs_do_return_delegation(inode, delegation);
2651da177e4SLinus Torvalds 	iput(inode);
2661da177e4SLinus Torvalds 	module_put_and_exit(0);
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds /*
2701da177e4SLinus Torvalds  * Asynchronous delegation recall!
2711da177e4SLinus Torvalds  */
2721da177e4SLinus Torvalds int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
2731da177e4SLinus Torvalds {
2741da177e4SLinus Torvalds 	struct recall_threadargs data = {
2751da177e4SLinus Torvalds 		.inode = inode,
2761da177e4SLinus Torvalds 		.stateid = stateid,
2771da177e4SLinus Torvalds 	};
2781da177e4SLinus Torvalds 	int status;
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	init_completion(&data.started);
2811da177e4SLinus Torvalds 	__module_get(THIS_MODULE);
2821da177e4SLinus Torvalds 	status = kernel_thread(recall_thread, &data, CLONE_KERNEL);
2831da177e4SLinus Torvalds 	if (status < 0)
2841da177e4SLinus Torvalds 		goto out_module_put;
2851da177e4SLinus Torvalds 	wait_for_completion(&data.started);
2861da177e4SLinus Torvalds 	return data.result;
2871da177e4SLinus Torvalds out_module_put:
2881da177e4SLinus Torvalds 	module_put(THIS_MODULE);
2891da177e4SLinus Torvalds 	return status;
2901da177e4SLinus Torvalds }
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds /*
2931da177e4SLinus Torvalds  * Retrieve the inode associated with a delegation
2941da177e4SLinus Torvalds  */
2951da177e4SLinus Torvalds struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle)
2961da177e4SLinus Torvalds {
2971da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
2981da177e4SLinus Torvalds 	struct inode *res = NULL;
2991da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
3001da177e4SLinus Torvalds 	list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
3011da177e4SLinus Torvalds 		if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
3021da177e4SLinus Torvalds 			res = igrab(delegation->inode);
3031da177e4SLinus Torvalds 			break;
3041da177e4SLinus Torvalds 		}
3051da177e4SLinus Torvalds 	}
3061da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
3071da177e4SLinus Torvalds 	return res;
3081da177e4SLinus Torvalds }
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds /*
3111da177e4SLinus Torvalds  * Mark all delegations as needing to be reclaimed
3121da177e4SLinus Torvalds  */
3131da177e4SLinus Torvalds void nfs_delegation_mark_reclaim(struct nfs4_client *clp)
3141da177e4SLinus Torvalds {
3151da177e4SLinus Torvalds 	struct nfs_delegation *delegation;
3161da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
3171da177e4SLinus Torvalds 	list_for_each_entry(delegation, &clp->cl_delegations, super_list)
3181da177e4SLinus Torvalds 		delegation->flags |= NFS_DELEGATION_NEED_RECLAIM;
3191da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
3201da177e4SLinus Torvalds }
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds /*
3231da177e4SLinus Torvalds  * Reap all unclaimed delegations after reboot recovery is done
3241da177e4SLinus Torvalds  */
3251da177e4SLinus Torvalds void nfs_delegation_reap_unclaimed(struct nfs4_client *clp)
3261da177e4SLinus Torvalds {
3271da177e4SLinus Torvalds 	struct nfs_delegation *delegation, *n;
3281da177e4SLinus Torvalds 	LIST_HEAD(head);
3291da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
3301da177e4SLinus Torvalds 	list_for_each_entry_safe(delegation, n, &clp->cl_delegations, super_list) {
3311da177e4SLinus Torvalds 		if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0)
3321da177e4SLinus Torvalds 			continue;
3331da177e4SLinus Torvalds 		list_move(&delegation->super_list, &head);
3341da177e4SLinus Torvalds 		NFS_I(delegation->inode)->delegation = NULL;
3351da177e4SLinus Torvalds 		NFS_I(delegation->inode)->delegation_state = 0;
3361da177e4SLinus Torvalds 	}
3371da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
3381da177e4SLinus Torvalds 	while(!list_empty(&head)) {
3391da177e4SLinus Torvalds 		delegation = list_entry(head.next, struct nfs_delegation, super_list);
3401da177e4SLinus Torvalds 		list_del(&delegation->super_list);
3411da177e4SLinus Torvalds 		nfs_free_delegation(delegation);
3421da177e4SLinus Torvalds 	}
3431da177e4SLinus Torvalds }
344