xref: /openbmc/linux/fs/nfs/nfs42proc.c (revision 4bdf87eb)
11c6dcbe5SAnna Schumaker /*
21c6dcbe5SAnna Schumaker  * Copyright (c) 2014 Anna Schumaker <Anna.Schumaker@Netapp.com>
31c6dcbe5SAnna Schumaker  */
41c6dcbe5SAnna Schumaker #include <linux/fs.h>
51c6dcbe5SAnna Schumaker #include <linux/sunrpc/sched.h>
61c6dcbe5SAnna Schumaker #include <linux/nfs.h>
71c6dcbe5SAnna Schumaker #include <linux/nfs3.h>
81c6dcbe5SAnna Schumaker #include <linux/nfs4.h>
91c6dcbe5SAnna Schumaker #include <linux/nfs_xdr.h>
101c6dcbe5SAnna Schumaker #include <linux/nfs_fs.h>
111c6dcbe5SAnna Schumaker #include "nfs4_fs.h"
121c6dcbe5SAnna Schumaker #include "nfs42.h"
131b4a4bd8SPeng Tao #include "iostat.h"
141b4a4bd8SPeng Tao #include "pnfs.h"
151b4a4bd8SPeng Tao #include "internal.h"
161b4a4bd8SPeng Tao 
17291e1b94SAnna Schumaker #define NFSDBG_FACILITY NFSDBG_PROC
181c6dcbe5SAnna Schumaker 
19f4ac1674SAnna Schumaker static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
204bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, loff_t len)
21f4ac1674SAnna Schumaker {
22f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
239a51940bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
24f4ac1674SAnna Schumaker 	struct nfs42_falloc_args args = {
25f4ac1674SAnna Schumaker 		.falloc_fh	= NFS_FH(inode),
26f4ac1674SAnna Schumaker 		.falloc_offset	= offset,
27f4ac1674SAnna Schumaker 		.falloc_length	= len,
289a51940bSAnna Schumaker 		.falloc_bitmask	= server->cache_consistency_bitmask,
29f4ac1674SAnna Schumaker 	};
309a51940bSAnna Schumaker 	struct nfs42_falloc_res res = {
319a51940bSAnna Schumaker 		.falloc_server	= server,
329a51940bSAnna Schumaker 	};
33f4ac1674SAnna Schumaker 	int status;
34f4ac1674SAnna Schumaker 
35f4ac1674SAnna Schumaker 	msg->rpc_argp = &args;
36f4ac1674SAnna Schumaker 	msg->rpc_resp = &res;
37f4ac1674SAnna Schumaker 
384bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.falloc_stateid, lock->open_context,
394bdf87ebSChristoph Hellwig 			lock, FMODE_WRITE);
40f4ac1674SAnna Schumaker 	if (status)
41f4ac1674SAnna Schumaker 		return status;
42f4ac1674SAnna Schumaker 
439a51940bSAnna Schumaker 	res.falloc_fattr = nfs_alloc_fattr();
449a51940bSAnna Schumaker 	if (!res.falloc_fattr)
459a51940bSAnna Schumaker 		return -ENOMEM;
469a51940bSAnna Schumaker 
479a51940bSAnna Schumaker 	status = nfs4_call_sync(server->client, server, msg,
48f4ac1674SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
499a51940bSAnna Schumaker 	if (status == 0)
509a51940bSAnna Schumaker 		status = nfs_post_op_update_inode(inode, res.falloc_fattr);
519a51940bSAnna Schumaker 
529a51940bSAnna Schumaker 	kfree(res.falloc_fattr);
539a51940bSAnna Schumaker 	return status;
54f4ac1674SAnna Schumaker }
55f4ac1674SAnna Schumaker 
56f4ac1674SAnna Schumaker static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
57f4ac1674SAnna Schumaker 				loff_t offset, loff_t len)
58f4ac1674SAnna Schumaker {
59f4ac1674SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
60f4ac1674SAnna Schumaker 	struct nfs4_exception exception = { };
614bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
62f4ac1674SAnna Schumaker 	int err;
63f4ac1674SAnna Schumaker 
644bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
654bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
664bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
674bdf87ebSChristoph Hellwig 
684bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
694bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
704bdf87ebSChristoph Hellwig 
71f4ac1674SAnna Schumaker 	do {
724bdf87ebSChristoph Hellwig 		err = _nfs42_proc_fallocate(msg, filep, lock, offset, len);
734bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
744bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
754bdf87ebSChristoph Hellwig 			break;
764bdf87ebSChristoph Hellwig 		}
77f4ac1674SAnna Schumaker 		err = nfs4_handle_exception(server, err, &exception);
78f4ac1674SAnna Schumaker 	} while (exception.retry);
79f4ac1674SAnna Schumaker 
804bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
81f4ac1674SAnna Schumaker 	return err;
82f4ac1674SAnna Schumaker }
83f4ac1674SAnna Schumaker 
84f4ac1674SAnna Schumaker int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
85f4ac1674SAnna Schumaker {
86f4ac1674SAnna Schumaker 	struct rpc_message msg = {
87f4ac1674SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE],
88f4ac1674SAnna Schumaker 	};
89f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
90f4ac1674SAnna Schumaker 	int err;
91f4ac1674SAnna Schumaker 
92f4ac1674SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE))
93f4ac1674SAnna Schumaker 		return -EOPNOTSUPP;
94f4ac1674SAnna Schumaker 
955955102cSAl Viro 	inode_lock(inode);
96f830f7ddSAnna Schumaker 
97f4ac1674SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
98f4ac1674SAnna Schumaker 	if (err == -EOPNOTSUPP)
99f4ac1674SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE;
100f830f7ddSAnna Schumaker 
1015955102cSAl Viro 	inode_unlock(inode);
102f4ac1674SAnna Schumaker 	return err;
103f4ac1674SAnna Schumaker }
104f4ac1674SAnna Schumaker 
105624bd5b7SAnna Schumaker int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
106624bd5b7SAnna Schumaker {
107624bd5b7SAnna Schumaker 	struct rpc_message msg = {
108624bd5b7SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE],
109624bd5b7SAnna Schumaker 	};
110624bd5b7SAnna Schumaker 	struct inode *inode = file_inode(filep);
111624bd5b7SAnna Schumaker 	int err;
112624bd5b7SAnna Schumaker 
113624bd5b7SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE))
114624bd5b7SAnna Schumaker 		return -EOPNOTSUPP;
115624bd5b7SAnna Schumaker 
1169a51940bSAnna Schumaker 	nfs_wb_all(inode);
1175955102cSAl Viro 	inode_lock(inode);
118f830f7ddSAnna Schumaker 
119624bd5b7SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
1209a51940bSAnna Schumaker 	if (err == 0)
1219a51940bSAnna Schumaker 		truncate_pagecache_range(inode, offset, (offset + len) -1);
122624bd5b7SAnna Schumaker 	if (err == -EOPNOTSUPP)
123624bd5b7SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE;
124f830f7ddSAnna Schumaker 
1255955102cSAl Viro 	inode_unlock(inode);
126624bd5b7SAnna Schumaker 	return err;
127624bd5b7SAnna Schumaker }
128624bd5b7SAnna Schumaker 
1294bdf87ebSChristoph Hellwig static loff_t _nfs42_proc_llseek(struct file *filep,
1304bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, int whence)
1311c6dcbe5SAnna Schumaker {
1321c6dcbe5SAnna Schumaker 	struct inode *inode = file_inode(filep);
1331c6dcbe5SAnna Schumaker 	struct nfs42_seek_args args = {
1341c6dcbe5SAnna Schumaker 		.sa_fh		= NFS_FH(inode),
1351c6dcbe5SAnna Schumaker 		.sa_offset	= offset,
1361c6dcbe5SAnna Schumaker 		.sa_what	= (whence == SEEK_HOLE) ?
1371c6dcbe5SAnna Schumaker 					NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA,
1381c6dcbe5SAnna Schumaker 	};
1391c6dcbe5SAnna Schumaker 	struct nfs42_seek_res res;
1401c6dcbe5SAnna Schumaker 	struct rpc_message msg = {
1411c6dcbe5SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK],
1421c6dcbe5SAnna Schumaker 		.rpc_argp = &args,
1431c6dcbe5SAnna Schumaker 		.rpc_resp = &res,
1441c6dcbe5SAnna Schumaker 	};
1451c6dcbe5SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
1461c6dcbe5SAnna Schumaker 	int status;
1471c6dcbe5SAnna Schumaker 
148878ffa9fSAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_SEEK))
1491c6dcbe5SAnna Schumaker 		return -ENOTSUPP;
1501c6dcbe5SAnna Schumaker 
1514bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context,
1524bdf87ebSChristoph Hellwig 			lock, FMODE_READ);
1531c6dcbe5SAnna Schumaker 	if (status)
1541c6dcbe5SAnna Schumaker 		return status;
1551c6dcbe5SAnna Schumaker 
1561c6dcbe5SAnna Schumaker 	nfs_wb_all(inode);
1571c6dcbe5SAnna Schumaker 	status = nfs4_call_sync(server->client, server, &msg,
1581c6dcbe5SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
1591c6dcbe5SAnna Schumaker 	if (status == -ENOTSUPP)
1601c6dcbe5SAnna Schumaker 		server->caps &= ~NFS_CAP_SEEK;
1611c6dcbe5SAnna Schumaker 	if (status)
1621c6dcbe5SAnna Schumaker 		return status;
1631c6dcbe5SAnna Schumaker 
1641c6dcbe5SAnna Schumaker 	return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes);
1651c6dcbe5SAnna Schumaker }
166be3a5d23STrond Myklebust 
167bdcc2cd1SJ. Bruce Fields loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
168bdcc2cd1SJ. Bruce Fields {
169bdcc2cd1SJ. Bruce Fields 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
170bdcc2cd1SJ. Bruce Fields 	struct nfs4_exception exception = { };
1714bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
172306a5549SJ. Bruce Fields 	loff_t err;
173bdcc2cd1SJ. Bruce Fields 
1744bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
1754bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
1764bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
1774bdf87ebSChristoph Hellwig 
1784bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
1794bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
1804bdf87ebSChristoph Hellwig 
181bdcc2cd1SJ. Bruce Fields 	do {
1824bdf87ebSChristoph Hellwig 		err = _nfs42_proc_llseek(filep, lock, offset, whence);
183306a5549SJ. Bruce Fields 		if (err >= 0)
184306a5549SJ. Bruce Fields 			break;
1854bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
1864bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
1874bdf87ebSChristoph Hellwig 			break;
1884bdf87ebSChristoph Hellwig 		}
189bdcc2cd1SJ. Bruce Fields 		err = nfs4_handle_exception(server, err, &exception);
190bdcc2cd1SJ. Bruce Fields 	} while (exception.retry);
191bdcc2cd1SJ. Bruce Fields 
1924bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
193bdcc2cd1SJ. Bruce Fields 	return err;
194bdcc2cd1SJ. Bruce Fields }
195bdcc2cd1SJ. Bruce Fields 
196bdcc2cd1SJ. Bruce Fields 
1971b4a4bd8SPeng Tao static void
1981b4a4bd8SPeng Tao nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata)
1991b4a4bd8SPeng Tao {
2001b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
2011b4a4bd8SPeng Tao 	struct nfs_server *server = NFS_SERVER(data->args.inode);
2021b4a4bd8SPeng Tao 
2031b4a4bd8SPeng Tao 	nfs41_setup_sequence(nfs4_get_session(server), &data->args.seq_args,
2041b4a4bd8SPeng Tao 			     &data->res.seq_res, task);
2051b4a4bd8SPeng Tao }
2061b4a4bd8SPeng Tao 
2071b4a4bd8SPeng Tao static void
2081b4a4bd8SPeng Tao nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
2091b4a4bd8SPeng Tao {
2101b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
21168d264cfSPeng Tao 	struct inode *inode = data->inode;
21268d264cfSPeng Tao 	struct pnfs_layout_hdr *lo;
2131b4a4bd8SPeng Tao 
2141b4a4bd8SPeng Tao 	if (!nfs4_sequence_done(task, &data->res.seq_res))
2151b4a4bd8SPeng Tao 		return;
2161b4a4bd8SPeng Tao 
2176c5a0d89STrond Myklebust 	switch (task->tk_status) {
2186c5a0d89STrond Myklebust 	case 0:
2196c5a0d89STrond Myklebust 		break;
22068d264cfSPeng Tao 	case -NFS4ERR_EXPIRED:
22168d264cfSPeng Tao 	case -NFS4ERR_STALE_STATEID:
22268d264cfSPeng Tao 	case -NFS4ERR_OLD_STATEID:
22368d264cfSPeng Tao 	case -NFS4ERR_BAD_STATEID:
22468d264cfSPeng Tao 		spin_lock(&inode->i_lock);
22568d264cfSPeng Tao 		lo = NFS_I(inode)->layout;
22668d264cfSPeng Tao 		if (lo && nfs4_stateid_match(&data->args.stateid,
22768d264cfSPeng Tao 					     &lo->plh_stateid)) {
22868d264cfSPeng Tao 			LIST_HEAD(head);
22968d264cfSPeng Tao 
23068d264cfSPeng Tao 			/*
23168d264cfSPeng Tao 			 * Mark the bad layout state as invalid, then retry
23268d264cfSPeng Tao 			 * with the current stateid.
23368d264cfSPeng Tao 			 */
23468d264cfSPeng Tao 			set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
23568d264cfSPeng Tao 			pnfs_mark_matching_lsegs_invalid(lo, &head, NULL);
23668d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
23768d264cfSPeng Tao 			pnfs_free_lseg_list(&head);
23868d264cfSPeng Tao 		} else
23968d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
24068d264cfSPeng Tao 		break;
2416c5a0d89STrond Myklebust 	case -ENOTSUPP:
2426c5a0d89STrond Myklebust 	case -EOPNOTSUPP:
24368d264cfSPeng Tao 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
2446c5a0d89STrond Myklebust 	default:
24568d264cfSPeng Tao 		break;
2461b4a4bd8SPeng Tao 	}
24768d264cfSPeng Tao 
24868d264cfSPeng Tao 	dprintk("%s server returns %d\n", __func__, task->tk_status);
2496c5a0d89STrond Myklebust }
2501b4a4bd8SPeng Tao 
2511b4a4bd8SPeng Tao static void
2521b4a4bd8SPeng Tao nfs42_layoutstat_release(void *calldata)
2531b4a4bd8SPeng Tao {
2541b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
2558733408dSPeng Tao 	struct nfs_server *nfss = NFS_SERVER(data->args.inode);
2568733408dSPeng Tao 
2578733408dSPeng Tao 	if (nfss->pnfs_curr_ld->cleanup_layoutstats)
2588733408dSPeng Tao 		nfss->pnfs_curr_ld->cleanup_layoutstats(data);
2591b4a4bd8SPeng Tao 
2601b4a4bd8SPeng Tao 	pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout);
2611bfe3b25SPeng Tao 	smp_mb__before_atomic();
2621bfe3b25SPeng Tao 	clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags);
2631bfe3b25SPeng Tao 	smp_mb__after_atomic();
2641b4a4bd8SPeng Tao 	nfs_iput_and_deactive(data->inode);
2651b4a4bd8SPeng Tao 	kfree(data->args.devinfo);
2661b4a4bd8SPeng Tao 	kfree(data);
2671b4a4bd8SPeng Tao }
2681b4a4bd8SPeng Tao 
269be3a5d23STrond Myklebust static const struct rpc_call_ops nfs42_layoutstat_ops = {
2701b4a4bd8SPeng Tao 	.rpc_call_prepare = nfs42_layoutstat_prepare,
2711b4a4bd8SPeng Tao 	.rpc_call_done = nfs42_layoutstat_done,
2721b4a4bd8SPeng Tao 	.rpc_release = nfs42_layoutstat_release,
273be3a5d23STrond Myklebust };
274be3a5d23STrond Myklebust 
275be3a5d23STrond Myklebust int nfs42_proc_layoutstats_generic(struct nfs_server *server,
276be3a5d23STrond Myklebust 				   struct nfs42_layoutstat_data *data)
277be3a5d23STrond Myklebust {
278be3a5d23STrond Myklebust 	struct rpc_message msg = {
279be3a5d23STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS],
280be3a5d23STrond Myklebust 		.rpc_argp = &data->args,
281be3a5d23STrond Myklebust 		.rpc_resp = &data->res,
282be3a5d23STrond Myklebust 	};
283be3a5d23STrond Myklebust 	struct rpc_task_setup task_setup = {
284be3a5d23STrond Myklebust 		.rpc_client = server->client,
285be3a5d23STrond Myklebust 		.rpc_message = &msg,
286be3a5d23STrond Myklebust 		.callback_ops = &nfs42_layoutstat_ops,
287be3a5d23STrond Myklebust 		.callback_data = data,
288be3a5d23STrond Myklebust 		.flags = RPC_TASK_ASYNC,
289be3a5d23STrond Myklebust 	};
290be3a5d23STrond Myklebust 	struct rpc_task *task;
291be3a5d23STrond Myklebust 
2921b4a4bd8SPeng Tao 	data->inode = nfs_igrab_and_active(data->args.inode);
2931b4a4bd8SPeng Tao 	if (!data->inode) {
2941b4a4bd8SPeng Tao 		nfs42_layoutstat_release(data);
2951b4a4bd8SPeng Tao 		return -EAGAIN;
2961b4a4bd8SPeng Tao 	}
297be3a5d23STrond Myklebust 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0);
298be3a5d23STrond Myklebust 	task = rpc_run_task(&task_setup);
299be3a5d23STrond Myklebust 	if (IS_ERR(task))
300be3a5d23STrond Myklebust 		return PTR_ERR(task);
301be3a5d23STrond Myklebust 	return 0;
302be3a5d23STrond Myklebust }
303e5341f3aSPeng Tao 
304e5341f3aSPeng Tao static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
3054bdf87ebSChristoph Hellwig 		struct file *dst_f, struct nfs_lock_context *src_lock,
3064bdf87ebSChristoph Hellwig 		struct nfs_lock_context *dst_lock, loff_t src_offset,
307e5341f3aSPeng Tao 		loff_t dst_offset, loff_t count)
308e5341f3aSPeng Tao {
309e5341f3aSPeng Tao 	struct inode *src_inode = file_inode(src_f);
310e5341f3aSPeng Tao 	struct inode *dst_inode = file_inode(dst_f);
311e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(dst_inode);
312e5341f3aSPeng Tao 	struct nfs42_clone_args args = {
313e5341f3aSPeng Tao 		.src_fh = NFS_FH(src_inode),
314e5341f3aSPeng Tao 		.dst_fh = NFS_FH(dst_inode),
315e5341f3aSPeng Tao 		.src_offset = src_offset,
316e5341f3aSPeng Tao 		.dst_offset = dst_offset,
3179494b2ceSChristoph Hellwig 		.count = count,
318e5341f3aSPeng Tao 		.dst_bitmask = server->cache_consistency_bitmask,
319e5341f3aSPeng Tao 	};
320e5341f3aSPeng Tao 	struct nfs42_clone_res res = {
321e5341f3aSPeng Tao 		.server	= server,
322e5341f3aSPeng Tao 	};
323e5341f3aSPeng Tao 	int status;
324e5341f3aSPeng Tao 
325e5341f3aSPeng Tao 	msg->rpc_argp = &args;
326e5341f3aSPeng Tao 	msg->rpc_resp = &res;
327e5341f3aSPeng Tao 
3284bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context,
3294bdf87ebSChristoph Hellwig 			src_lock, FMODE_READ);
330e5341f3aSPeng Tao 	if (status)
331e5341f3aSPeng Tao 		return status;
332e5341f3aSPeng Tao 
3334bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
3344bdf87ebSChristoph Hellwig 			dst_lock, FMODE_WRITE);
335e5341f3aSPeng Tao 	if (status)
336e5341f3aSPeng Tao 		return status;
337e5341f3aSPeng Tao 
338e5341f3aSPeng Tao 	res.dst_fattr = nfs_alloc_fattr();
339e5341f3aSPeng Tao 	if (!res.dst_fattr)
340e5341f3aSPeng Tao 		return -ENOMEM;
341e5341f3aSPeng Tao 
342e5341f3aSPeng Tao 	status = nfs4_call_sync(server->client, server, msg,
343e5341f3aSPeng Tao 				&args.seq_args, &res.seq_res, 0);
344e5341f3aSPeng Tao 	if (status == 0)
345e5341f3aSPeng Tao 		status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
346e5341f3aSPeng Tao 
347e5341f3aSPeng Tao 	kfree(res.dst_fattr);
348e5341f3aSPeng Tao 	return status;
349e5341f3aSPeng Tao }
350e5341f3aSPeng Tao 
351e5341f3aSPeng Tao int nfs42_proc_clone(struct file *src_f, struct file *dst_f,
352e5341f3aSPeng Tao 		     loff_t src_offset, loff_t dst_offset, loff_t count)
353e5341f3aSPeng Tao {
354e5341f3aSPeng Tao 	struct rpc_message msg = {
355e5341f3aSPeng Tao 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE],
356e5341f3aSPeng Tao 	};
357e5341f3aSPeng Tao 	struct inode *inode = file_inode(src_f);
358e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(file_inode(src_f));
3594bdf87ebSChristoph Hellwig 	struct nfs_lock_context *src_lock;
3604bdf87ebSChristoph Hellwig 	struct nfs_lock_context *dst_lock;
3614bdf87ebSChristoph Hellwig 	struct nfs4_exception src_exception = { };
3624bdf87ebSChristoph Hellwig 	struct nfs4_exception dst_exception = { };
3634bdf87ebSChristoph Hellwig 	int err, err2;
364e5341f3aSPeng Tao 
365e5341f3aSPeng Tao 	if (!nfs_server_capable(inode, NFS_CAP_CLONE))
366e5341f3aSPeng Tao 		return -EOPNOTSUPP;
367e5341f3aSPeng Tao 
3684bdf87ebSChristoph Hellwig 	src_lock = nfs_get_lock_context(nfs_file_open_context(src_f));
3694bdf87ebSChristoph Hellwig 	if (IS_ERR(src_lock))
3704bdf87ebSChristoph Hellwig 		return PTR_ERR(src_lock);
3714bdf87ebSChristoph Hellwig 
3724bdf87ebSChristoph Hellwig 	src_exception.inode = file_inode(src_f);
3734bdf87ebSChristoph Hellwig 	src_exception.state = src_lock->open_context->state;
3744bdf87ebSChristoph Hellwig 
3754bdf87ebSChristoph Hellwig 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f));
3764bdf87ebSChristoph Hellwig 	if (IS_ERR(dst_lock)) {
3774bdf87ebSChristoph Hellwig 		err = PTR_ERR(dst_lock);
3784bdf87ebSChristoph Hellwig 		goto out_put_src_lock;
3794bdf87ebSChristoph Hellwig 	}
3804bdf87ebSChristoph Hellwig 
3814bdf87ebSChristoph Hellwig 	dst_exception.inode = file_inode(dst_f);
3824bdf87ebSChristoph Hellwig 	dst_exception.state = dst_lock->open_context->state;
3834bdf87ebSChristoph Hellwig 
384e5341f3aSPeng Tao 	do {
3854bdf87ebSChristoph Hellwig 		err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock,
3864bdf87ebSChristoph Hellwig 					src_offset, dst_offset, count);
387e5341f3aSPeng Tao 		if (err == -ENOTSUPP || err == -EOPNOTSUPP) {
388e5341f3aSPeng Tao 			NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE;
3894bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
3904bdf87ebSChristoph Hellwig 			break;
391e5341f3aSPeng Tao 		}
392e5341f3aSPeng Tao 
3934bdf87ebSChristoph Hellwig 		err2 = nfs4_handle_exception(server, err, &src_exception);
3944bdf87ebSChristoph Hellwig 		err = nfs4_handle_exception(server, err, &dst_exception);
3954bdf87ebSChristoph Hellwig 		if (!err)
3964bdf87ebSChristoph Hellwig 			err = err2;
3974bdf87ebSChristoph Hellwig 	} while (src_exception.retry || dst_exception.retry);
3984bdf87ebSChristoph Hellwig 
3994bdf87ebSChristoph Hellwig 	nfs_put_lock_context(dst_lock);
4004bdf87ebSChristoph Hellwig out_put_src_lock:
4014bdf87ebSChristoph Hellwig 	nfs_put_lock_context(src_lock);
402e5341f3aSPeng Tao 	return err;
403e5341f3aSPeng Tao }
404