xref: /openbmc/linux/fs/nfs/nfs42proc.c (revision 1d38f3f0)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21c6dcbe5SAnna Schumaker /*
31c6dcbe5SAnna Schumaker  * Copyright (c) 2014 Anna Schumaker <Anna.Schumaker@Netapp.com>
41c6dcbe5SAnna Schumaker  */
51c6dcbe5SAnna Schumaker #include <linux/fs.h>
60491567bSOlga Kornievskaia #include <linux/sunrpc/addr.h>
71c6dcbe5SAnna Schumaker #include <linux/sunrpc/sched.h>
81c6dcbe5SAnna Schumaker #include <linux/nfs.h>
91c6dcbe5SAnna Schumaker #include <linux/nfs3.h>
101c6dcbe5SAnna Schumaker #include <linux/nfs4.h>
111c6dcbe5SAnna Schumaker #include <linux/nfs_xdr.h>
121c6dcbe5SAnna Schumaker #include <linux/nfs_fs.h>
131c6dcbe5SAnna Schumaker #include "nfs4_fs.h"
141c6dcbe5SAnna Schumaker #include "nfs42.h"
151b4a4bd8SPeng Tao #include "iostat.h"
161b4a4bd8SPeng Tao #include "pnfs.h"
17efc6f4aaSAnna Schumaker #include "nfs4session.h"
181b4a4bd8SPeng Tao #include "internal.h"
190491567bSOlga Kornievskaia #include "delegation.h"
201b4a4bd8SPeng Tao 
21291e1b94SAnna Schumaker #define NFSDBG_FACILITY NFSDBG_PROC
22c975c209SOlga Kornievskaia static int nfs42_do_offload_cancel_async(struct file *dst, nfs4_stateid *std);
231c6dcbe5SAnna Schumaker 
240491567bSOlga Kornievskaia static void nfs42_set_netaddr(struct file *filep, struct nfs42_netaddr *naddr)
250491567bSOlga Kornievskaia {
260491567bSOlga Kornievskaia 	struct nfs_client *clp = (NFS_SERVER(file_inode(filep)))->nfs_client;
270491567bSOlga Kornievskaia 	unsigned short port = 2049;
280491567bSOlga Kornievskaia 
290491567bSOlga Kornievskaia 	rcu_read_lock();
300491567bSOlga Kornievskaia 	naddr->netid_len = scnprintf(naddr->netid,
310491567bSOlga Kornievskaia 					sizeof(naddr->netid), "%s",
320491567bSOlga Kornievskaia 					rpc_peeraddr2str(clp->cl_rpcclient,
330491567bSOlga Kornievskaia 					RPC_DISPLAY_NETID));
340491567bSOlga Kornievskaia 	naddr->addr_len = scnprintf(naddr->addr,
350491567bSOlga Kornievskaia 					sizeof(naddr->addr),
360491567bSOlga Kornievskaia 					"%s.%u.%u",
370491567bSOlga Kornievskaia 					rpc_peeraddr2str(clp->cl_rpcclient,
380491567bSOlga Kornievskaia 					RPC_DISPLAY_ADDR),
390491567bSOlga Kornievskaia 					port >> 8, port & 255);
400491567bSOlga Kornievskaia 	rcu_read_unlock();
410491567bSOlga Kornievskaia }
420491567bSOlga Kornievskaia 
43f4ac1674SAnna Schumaker static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
444bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, loff_t len)
45f4ac1674SAnna Schumaker {
46f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
479a51940bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
48f4ac1674SAnna Schumaker 	struct nfs42_falloc_args args = {
49f4ac1674SAnna Schumaker 		.falloc_fh	= NFS_FH(inode),
50f4ac1674SAnna Schumaker 		.falloc_offset	= offset,
51f4ac1674SAnna Schumaker 		.falloc_length	= len,
529a51940bSAnna Schumaker 		.falloc_bitmask	= server->cache_consistency_bitmask,
53f4ac1674SAnna Schumaker 	};
549a51940bSAnna Schumaker 	struct nfs42_falloc_res res = {
559a51940bSAnna Schumaker 		.falloc_server	= server,
569a51940bSAnna Schumaker 	};
57f4ac1674SAnna Schumaker 	int status;
58f4ac1674SAnna Schumaker 
59f4ac1674SAnna Schumaker 	msg->rpc_argp = &args;
60f4ac1674SAnna Schumaker 	msg->rpc_resp = &res;
61f4ac1674SAnna Schumaker 
624bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.falloc_stateid, lock->open_context,
634bdf87ebSChristoph Hellwig 			lock, FMODE_WRITE);
64f4ac1674SAnna Schumaker 	if (status)
65f4ac1674SAnna Schumaker 		return status;
66f4ac1674SAnna Schumaker 
679a51940bSAnna Schumaker 	res.falloc_fattr = nfs_alloc_fattr();
689a51940bSAnna Schumaker 	if (!res.falloc_fattr)
699a51940bSAnna Schumaker 		return -ENOMEM;
709a51940bSAnna Schumaker 
719a51940bSAnna Schumaker 	status = nfs4_call_sync(server->client, server, msg,
72f4ac1674SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
739a51940bSAnna Schumaker 	if (status == 0)
749a51940bSAnna Schumaker 		status = nfs_post_op_update_inode(inode, res.falloc_fattr);
759a51940bSAnna Schumaker 
769a51940bSAnna Schumaker 	kfree(res.falloc_fattr);
779a51940bSAnna Schumaker 	return status;
78f4ac1674SAnna Schumaker }
79f4ac1674SAnna Schumaker 
80f4ac1674SAnna Schumaker static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
81f4ac1674SAnna Schumaker 				loff_t offset, loff_t len)
82f4ac1674SAnna Schumaker {
83f4ac1674SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
84f4ac1674SAnna Schumaker 	struct nfs4_exception exception = { };
854bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
86f4ac1674SAnna Schumaker 	int err;
87f4ac1674SAnna Schumaker 
884bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
894bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
904bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
914bdf87ebSChristoph Hellwig 
924bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
934bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
944bdf87ebSChristoph Hellwig 
95f4ac1674SAnna Schumaker 	do {
964bdf87ebSChristoph Hellwig 		err = _nfs42_proc_fallocate(msg, filep, lock, offset, len);
974bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
984bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
994bdf87ebSChristoph Hellwig 			break;
1004bdf87ebSChristoph Hellwig 		}
101f4ac1674SAnna Schumaker 		err = nfs4_handle_exception(server, err, &exception);
102f4ac1674SAnna Schumaker 	} while (exception.retry);
103f4ac1674SAnna Schumaker 
1044bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
105f4ac1674SAnna Schumaker 	return err;
106f4ac1674SAnna Schumaker }
107f4ac1674SAnna Schumaker 
108f4ac1674SAnna Schumaker int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
109f4ac1674SAnna Schumaker {
110f4ac1674SAnna Schumaker 	struct rpc_message msg = {
111f4ac1674SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE],
112f4ac1674SAnna Schumaker 	};
113f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
114f4ac1674SAnna Schumaker 	int err;
115f4ac1674SAnna Schumaker 
116f4ac1674SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE))
117f4ac1674SAnna Schumaker 		return -EOPNOTSUPP;
118f4ac1674SAnna Schumaker 
1195955102cSAl Viro 	inode_lock(inode);
120f830f7ddSAnna Schumaker 
121f4ac1674SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
122f4ac1674SAnna Schumaker 	if (err == -EOPNOTSUPP)
123f4ac1674SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE;
124f830f7ddSAnna Schumaker 
1255955102cSAl Viro 	inode_unlock(inode);
126f4ac1674SAnna Schumaker 	return err;
127f4ac1674SAnna Schumaker }
128f4ac1674SAnna Schumaker 
129624bd5b7SAnna Schumaker int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
130624bd5b7SAnna Schumaker {
131624bd5b7SAnna Schumaker 	struct rpc_message msg = {
132624bd5b7SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE],
133624bd5b7SAnna Schumaker 	};
134624bd5b7SAnna Schumaker 	struct inode *inode = file_inode(filep);
135624bd5b7SAnna Schumaker 	int err;
136624bd5b7SAnna Schumaker 
137624bd5b7SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE))
138624bd5b7SAnna Schumaker 		return -EOPNOTSUPP;
139624bd5b7SAnna Schumaker 
1405955102cSAl Viro 	inode_lock(inode);
1411e564d3dSTrond Myklebust 	err = nfs_sync_inode(inode);
1421e564d3dSTrond Myklebust 	if (err)
1431e564d3dSTrond Myklebust 		goto out_unlock;
144f830f7ddSAnna Schumaker 
145624bd5b7SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
1469a51940bSAnna Schumaker 	if (err == 0)
1479a51940bSAnna Schumaker 		truncate_pagecache_range(inode, offset, (offset + len) -1);
148624bd5b7SAnna Schumaker 	if (err == -EOPNOTSUPP)
149624bd5b7SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE;
1501e564d3dSTrond Myklebust out_unlock:
1515955102cSAl Viro 	inode_unlock(inode);
152624bd5b7SAnna Schumaker 	return err;
153624bd5b7SAnna Schumaker }
154624bd5b7SAnna Schumaker 
15562164f31SOlga Kornievskaia static int handle_async_copy(struct nfs42_copy_res *res,
15662164f31SOlga Kornievskaia 			     struct nfs_server *server,
15762164f31SOlga Kornievskaia 			     struct file *src,
15862164f31SOlga Kornievskaia 			     struct file *dst,
15962164f31SOlga Kornievskaia 			     nfs4_stateid *src_stateid)
16062164f31SOlga Kornievskaia {
16199f2c555SOlga Kornievskaia 	struct nfs4_copy_state *copy, *tmp_copy;
16262164f31SOlga Kornievskaia 	int status = NFS4_OK;
163bc0c9079SOlga Kornievskaia 	bool found_pending = false;
164e4648aa4SOlga Kornievskaia 	struct nfs_open_context *ctx = nfs_file_open_context(dst);
165bc0c9079SOlga Kornievskaia 
16699f2c555SOlga Kornievskaia 	copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
16799f2c555SOlga Kornievskaia 	if (!copy)
16899f2c555SOlga Kornievskaia 		return -ENOMEM;
16999f2c555SOlga Kornievskaia 
170bc0c9079SOlga Kornievskaia 	spin_lock(&server->nfs_client->cl_lock);
17199f2c555SOlga Kornievskaia 	list_for_each_entry(tmp_copy, &server->nfs_client->pending_cb_stateids,
172bc0c9079SOlga Kornievskaia 				copies) {
17399f2c555SOlga Kornievskaia 		if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
174bc0c9079SOlga Kornievskaia 				NFS4_STATEID_SIZE))
175bc0c9079SOlga Kornievskaia 			continue;
176bc0c9079SOlga Kornievskaia 		found_pending = true;
17799f2c555SOlga Kornievskaia 		list_del(&tmp_copy->copies);
178bc0c9079SOlga Kornievskaia 		break;
179bc0c9079SOlga Kornievskaia 	}
180bc0c9079SOlga Kornievskaia 	if (found_pending) {
181bc0c9079SOlga Kornievskaia 		spin_unlock(&server->nfs_client->cl_lock);
18299f2c555SOlga Kornievskaia 		kfree(copy);
18399f2c555SOlga Kornievskaia 		copy = tmp_copy;
184bc0c9079SOlga Kornievskaia 		goto out;
185bc0c9079SOlga Kornievskaia 	}
18662164f31SOlga Kornievskaia 
18762164f31SOlga Kornievskaia 	memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
18862164f31SOlga Kornievskaia 	init_completion(&copy->completion);
189e4648aa4SOlga Kornievskaia 	copy->parent_state = ctx->state;
19062164f31SOlga Kornievskaia 
19162164f31SOlga Kornievskaia 	list_add_tail(&copy->copies, &server->ss_copies);
19262164f31SOlga Kornievskaia 	spin_unlock(&server->nfs_client->cl_lock);
19362164f31SOlga Kornievskaia 
194c975c209SOlga Kornievskaia 	status = wait_for_completion_interruptible(&copy->completion);
19562164f31SOlga Kornievskaia 	spin_lock(&server->nfs_client->cl_lock);
19662164f31SOlga Kornievskaia 	list_del_init(&copy->copies);
19762164f31SOlga Kornievskaia 	spin_unlock(&server->nfs_client->cl_lock);
198c975c209SOlga Kornievskaia 	if (status == -ERESTARTSYS) {
199e4648aa4SOlga Kornievskaia 		goto out_cancel;
200e4648aa4SOlga Kornievskaia 	} else if (copy->flags) {
201e4648aa4SOlga Kornievskaia 		status = -EAGAIN;
202e4648aa4SOlga Kornievskaia 		goto out_cancel;
203c975c209SOlga Kornievskaia 	}
204bc0c9079SOlga Kornievskaia out:
20562164f31SOlga Kornievskaia 	res->write_res.count = copy->count;
20662164f31SOlga Kornievskaia 	memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
20762164f31SOlga Kornievskaia 	status = -copy->error;
20862164f31SOlga Kornievskaia 
20962164f31SOlga Kornievskaia 	kfree(copy);
21062164f31SOlga Kornievskaia 	return status;
211e4648aa4SOlga Kornievskaia out_cancel:
212e4648aa4SOlga Kornievskaia 	nfs42_do_offload_cancel_async(dst, &copy->stateid);
213e4648aa4SOlga Kornievskaia 	kfree(copy);
214e4648aa4SOlga Kornievskaia 	return status;
21562164f31SOlga Kornievskaia }
21662164f31SOlga Kornievskaia 
2176b8d84e2SOlga Kornievskaia static int process_copy_commit(struct file *dst, loff_t pos_dst,
2186b8d84e2SOlga Kornievskaia 			       struct nfs42_copy_res *res)
2196b8d84e2SOlga Kornievskaia {
2206b8d84e2SOlga Kornievskaia 	struct nfs_commitres cres;
2216b8d84e2SOlga Kornievskaia 	int status = -ENOMEM;
2226b8d84e2SOlga Kornievskaia 
2236b8d84e2SOlga Kornievskaia 	cres.verf = kzalloc(sizeof(struct nfs_writeverf), GFP_NOFS);
2246b8d84e2SOlga Kornievskaia 	if (!cres.verf)
2256b8d84e2SOlga Kornievskaia 		goto out;
2266b8d84e2SOlga Kornievskaia 
2276b8d84e2SOlga Kornievskaia 	status = nfs4_proc_commit(dst, pos_dst, res->write_res.count, &cres);
2286b8d84e2SOlga Kornievskaia 	if (status)
2296b8d84e2SOlga Kornievskaia 		goto out_free;
2306b8d84e2SOlga Kornievskaia 	if (nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
2316b8d84e2SOlga Kornievskaia 				    &cres.verf->verifier)) {
2326b8d84e2SOlga Kornievskaia 		dprintk("commit verf differs from copy verf\n");
2336b8d84e2SOlga Kornievskaia 		status = -EAGAIN;
2346b8d84e2SOlga Kornievskaia 	}
2356b8d84e2SOlga Kornievskaia out_free:
2366b8d84e2SOlga Kornievskaia 	kfree(cres.verf);
2376b8d84e2SOlga Kornievskaia out:
2386b8d84e2SOlga Kornievskaia 	return status;
2396b8d84e2SOlga Kornievskaia }
2406b8d84e2SOlga Kornievskaia 
2419d8cacbfSTrond Myklebust static ssize_t _nfs42_proc_copy(struct file *src,
2422e72448bSAnna Schumaker 				struct nfs_lock_context *src_lock,
2439d8cacbfSTrond Myklebust 				struct file *dst,
2442e72448bSAnna Schumaker 				struct nfs_lock_context *dst_lock,
2459d8cacbfSTrond Myklebust 				struct nfs42_copy_args *args,
2461d38f3f0SOlga Kornievskaia 				struct nfs42_copy_res *res,
2471d38f3f0SOlga Kornievskaia 				struct nl4_server *nss,
2481d38f3f0SOlga Kornievskaia 				nfs4_stateid *cnr_stateid)
2492e72448bSAnna Schumaker {
2502e72448bSAnna Schumaker 	struct rpc_message msg = {
2512e72448bSAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY],
2529d8cacbfSTrond Myklebust 		.rpc_argp = args,
2539d8cacbfSTrond Myklebust 		.rpc_resp = res,
2542e72448bSAnna Schumaker 	};
2552e72448bSAnna Schumaker 	struct inode *dst_inode = file_inode(dst);
2562e72448bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(dst_inode);
2579d8cacbfSTrond Myklebust 	loff_t pos_src = args->src_pos;
2589d8cacbfSTrond Myklebust 	loff_t pos_dst = args->dst_pos;
2599d8cacbfSTrond Myklebust 	size_t count = args->count;
2601ee48bddSOlga Kornievskaia 	ssize_t status;
2612e72448bSAnna Schumaker 
2621d38f3f0SOlga Kornievskaia 	if (nss) {
2631d38f3f0SOlga Kornievskaia 		args->cp_src = nss;
2641d38f3f0SOlga Kornievskaia 		nfs4_stateid_copy(&args->src_stateid, cnr_stateid);
2651d38f3f0SOlga Kornievskaia 	} else {
2661d38f3f0SOlga Kornievskaia 		status = nfs4_set_rw_stateid(&args->src_stateid,
2671d38f3f0SOlga Kornievskaia 				src_lock->open_context, src_lock, FMODE_READ);
2682e72448bSAnna Schumaker 		if (status)
2692e72448bSAnna Schumaker 			return status;
2701d38f3f0SOlga Kornievskaia 	}
271837bb1d7STrond Myklebust 	status = nfs_filemap_write_and_wait_range(file_inode(src)->i_mapping,
272837bb1d7STrond Myklebust 			pos_src, pos_src + (loff_t)count - 1);
273837bb1d7STrond Myklebust 	if (status)
274837bb1d7STrond Myklebust 		return status;
275837bb1d7STrond Myklebust 
2769d8cacbfSTrond Myklebust 	status = nfs4_set_rw_stateid(&args->dst_stateid, dst_lock->open_context,
2772e72448bSAnna Schumaker 				     dst_lock, FMODE_WRITE);
2782e72448bSAnna Schumaker 	if (status)
2792e72448bSAnna Schumaker 		return status;
2802e72448bSAnna Schumaker 
281837bb1d7STrond Myklebust 	status = nfs_sync_inode(dst_inode);
282837bb1d7STrond Myklebust 	if (status)
283837bb1d7STrond Myklebust 		return status;
284837bb1d7STrond Myklebust 
28562164f31SOlga Kornievskaia 	res->commit_res.verf = NULL;
28662164f31SOlga Kornievskaia 	if (args->sync) {
28762164f31SOlga Kornievskaia 		res->commit_res.verf =
28862164f31SOlga Kornievskaia 			kzalloc(sizeof(struct nfs_writeverf), GFP_NOFS);
289e0926934SOlga Kornievskaia 		if (!res->commit_res.verf)
290e0926934SOlga Kornievskaia 			return -ENOMEM;
29162164f31SOlga Kornievskaia 	}
292e4648aa4SOlga Kornievskaia 	set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
293e4648aa4SOlga Kornievskaia 		&dst_lock->open_context->state->flags);
294e4648aa4SOlga Kornievskaia 
2952e72448bSAnna Schumaker 	status = nfs4_call_sync(server->client, server, &msg,
2969d8cacbfSTrond Myklebust 				&args->seq_args, &res->seq_res, 0);
2972e72448bSAnna Schumaker 	if (status == -ENOTSUPP)
2982e72448bSAnna Schumaker 		server->caps &= ~NFS_CAP_COPY;
2992e72448bSAnna Schumaker 	if (status)
300e0926934SOlga Kornievskaia 		goto out;
3012e72448bSAnna Schumaker 
30262164f31SOlga Kornievskaia 	if (args->sync &&
30362164f31SOlga Kornievskaia 		nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
304e0926934SOlga Kornievskaia 				    &res->commit_res.verf->verifier)) {
305e0926934SOlga Kornievskaia 		status = -EAGAIN;
306e0926934SOlga Kornievskaia 		goto out;
3072e72448bSAnna Schumaker 	}
3082e72448bSAnna Schumaker 
30962164f31SOlga Kornievskaia 	if (!res->synchronous) {
31062164f31SOlga Kornievskaia 		status = handle_async_copy(res, server, src, dst,
31162164f31SOlga Kornievskaia 				&args->src_stateid);
31262164f31SOlga Kornievskaia 		if (status)
31362164f31SOlga Kornievskaia 			return status;
31462164f31SOlga Kornievskaia 	}
31562164f31SOlga Kornievskaia 
3166b8d84e2SOlga Kornievskaia 	if ((!res->synchronous || !args->sync) &&
3176b8d84e2SOlga Kornievskaia 			res->write_res.verifier.committed != NFS_FILE_SYNC) {
3186b8d84e2SOlga Kornievskaia 		status = process_copy_commit(dst, pos_dst, res);
3196b8d84e2SOlga Kornievskaia 		if (status)
3206b8d84e2SOlga Kornievskaia 			return status;
3216b8d84e2SOlga Kornievskaia 	}
3226b8d84e2SOlga Kornievskaia 
3232e72448bSAnna Schumaker 	truncate_pagecache_range(dst_inode, pos_dst,
3249d8cacbfSTrond Myklebust 				 pos_dst + res->write_res.count);
3252e72448bSAnna Schumaker 
326e0926934SOlga Kornievskaia 	status = res->write_res.count;
327e0926934SOlga Kornievskaia out:
32862164f31SOlga Kornievskaia 	if (args->sync)
329e0926934SOlga Kornievskaia 		kfree(res->commit_res.verf);
330e0926934SOlga Kornievskaia 	return status;
3312e72448bSAnna Schumaker }
3322e72448bSAnna Schumaker 
3332e72448bSAnna Schumaker ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
3341d38f3f0SOlga Kornievskaia 			struct file *dst, loff_t pos_dst, size_t count,
3351d38f3f0SOlga Kornievskaia 			struct nl4_server *nss,
3361d38f3f0SOlga Kornievskaia 			nfs4_stateid *cnr_stateid)
3372e72448bSAnna Schumaker {
3382e72448bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(dst));
3392e72448bSAnna Schumaker 	struct nfs_lock_context *src_lock;
3402e72448bSAnna Schumaker 	struct nfs_lock_context *dst_lock;
3419d8cacbfSTrond Myklebust 	struct nfs42_copy_args args = {
3429d8cacbfSTrond Myklebust 		.src_fh		= NFS_FH(file_inode(src)),
3439d8cacbfSTrond Myklebust 		.src_pos	= pos_src,
3449d8cacbfSTrond Myklebust 		.dst_fh		= NFS_FH(file_inode(dst)),
3459d8cacbfSTrond Myklebust 		.dst_pos	= pos_dst,
3469d8cacbfSTrond Myklebust 		.count		= count,
34762164f31SOlga Kornievskaia 		.sync		= false,
3489d8cacbfSTrond Myklebust 	};
3499d8cacbfSTrond Myklebust 	struct nfs42_copy_res res;
3509d8cacbfSTrond Myklebust 	struct nfs4_exception src_exception = {
3519d8cacbfSTrond Myklebust 		.inode		= file_inode(src),
3529d8cacbfSTrond Myklebust 		.stateid	= &args.src_stateid,
3539d8cacbfSTrond Myklebust 	};
3549d8cacbfSTrond Myklebust 	struct nfs4_exception dst_exception = {
3559d8cacbfSTrond Myklebust 		.inode		= file_inode(dst),
3569d8cacbfSTrond Myklebust 		.stateid	= &args.dst_stateid,
3579d8cacbfSTrond Myklebust 	};
3582e72448bSAnna Schumaker 	ssize_t err, err2;
3592e72448bSAnna Schumaker 
3602e72448bSAnna Schumaker 	src_lock = nfs_get_lock_context(nfs_file_open_context(src));
3612e72448bSAnna Schumaker 	if (IS_ERR(src_lock))
3622e72448bSAnna Schumaker 		return PTR_ERR(src_lock);
3632e72448bSAnna Schumaker 
3642e72448bSAnna Schumaker 	src_exception.state = src_lock->open_context->state;
3652e72448bSAnna Schumaker 
3662e72448bSAnna Schumaker 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst));
3672e72448bSAnna Schumaker 	if (IS_ERR(dst_lock)) {
3682e72448bSAnna Schumaker 		err = PTR_ERR(dst_lock);
3692e72448bSAnna Schumaker 		goto out_put_src_lock;
3702e72448bSAnna Schumaker 	}
3712e72448bSAnna Schumaker 
3722e72448bSAnna Schumaker 	dst_exception.state = dst_lock->open_context->state;
3732e72448bSAnna Schumaker 
3742e72448bSAnna Schumaker 	do {
375ea8ea737SLinus Torvalds 		inode_lock(file_inode(dst));
3769d8cacbfSTrond Myklebust 		err = _nfs42_proc_copy(src, src_lock,
3779d8cacbfSTrond Myklebust 				dst, dst_lock,
3781d38f3f0SOlga Kornievskaia 				&args, &res,
3791d38f3f0SOlga Kornievskaia 				nss, cnr_stateid);
380ea8ea737SLinus Torvalds 		inode_unlock(file_inode(dst));
3812e72448bSAnna Schumaker 
3829d8cacbfSTrond Myklebust 		if (err >= 0)
3839d8cacbfSTrond Myklebust 			break;
3842e72448bSAnna Schumaker 		if (err == -ENOTSUPP) {
3852e72448bSAnna Schumaker 			err = -EOPNOTSUPP;
3862e72448bSAnna Schumaker 			break;
387539f57b3SOlga Kornievskaia 		} else if (err == -EAGAIN) {
388539f57b3SOlga Kornievskaia 			dst_exception.retry = 1;
389539f57b3SOlga Kornievskaia 			continue;
390539f57b3SOlga Kornievskaia 		} else if (err == -NFS4ERR_OFFLOAD_NO_REQS && !args.sync) {
391539f57b3SOlga Kornievskaia 			args.sync = true;
392e0926934SOlga Kornievskaia 			dst_exception.retry = 1;
393e0926934SOlga Kornievskaia 			continue;
3942e72448bSAnna Schumaker 		}
3952e72448bSAnna Schumaker 
3962e72448bSAnna Schumaker 		err2 = nfs4_handle_exception(server, err, &src_exception);
3972e72448bSAnna Schumaker 		err  = nfs4_handle_exception(server, err, &dst_exception);
3982e72448bSAnna Schumaker 		if (!err)
3992e72448bSAnna Schumaker 			err = err2;
4002e72448bSAnna Schumaker 	} while (src_exception.retry || dst_exception.retry);
4012e72448bSAnna Schumaker 
4022e72448bSAnna Schumaker 	nfs_put_lock_context(dst_lock);
4032e72448bSAnna Schumaker out_put_src_lock:
4042e72448bSAnna Schumaker 	nfs_put_lock_context(src_lock);
4052e72448bSAnna Schumaker 	return err;
4062e72448bSAnna Schumaker }
4072e72448bSAnna Schumaker 
408c975c209SOlga Kornievskaia struct nfs42_offloadcancel_data {
409c975c209SOlga Kornievskaia 	struct nfs_server *seq_server;
410c975c209SOlga Kornievskaia 	struct nfs42_offload_status_args args;
411c975c209SOlga Kornievskaia 	struct nfs42_offload_status_res res;
412c975c209SOlga Kornievskaia };
413c975c209SOlga Kornievskaia 
414c975c209SOlga Kornievskaia static void nfs42_offload_cancel_prepare(struct rpc_task *task, void *calldata)
415c975c209SOlga Kornievskaia {
416c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
417c975c209SOlga Kornievskaia 
418c975c209SOlga Kornievskaia 	nfs4_setup_sequence(data->seq_server->nfs_client,
419c975c209SOlga Kornievskaia 				&data->args.osa_seq_args,
420c975c209SOlga Kornievskaia 				&data->res.osr_seq_res, task);
421c975c209SOlga Kornievskaia }
422c975c209SOlga Kornievskaia 
423c975c209SOlga Kornievskaia static void nfs42_offload_cancel_done(struct rpc_task *task, void *calldata)
424c975c209SOlga Kornievskaia {
425c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
426c975c209SOlga Kornievskaia 
427c975c209SOlga Kornievskaia 	nfs41_sequence_done(task, &data->res.osr_seq_res);
428c975c209SOlga Kornievskaia 	if (task->tk_status &&
429c975c209SOlga Kornievskaia 		nfs4_async_handle_error(task, data->seq_server, NULL,
430c975c209SOlga Kornievskaia 			NULL) == -EAGAIN)
431c975c209SOlga Kornievskaia 		rpc_restart_call_prepare(task);
432c975c209SOlga Kornievskaia }
433c975c209SOlga Kornievskaia 
434c975c209SOlga Kornievskaia static void nfs42_free_offloadcancel_data(void *data)
435c975c209SOlga Kornievskaia {
436c975c209SOlga Kornievskaia 	kfree(data);
437c975c209SOlga Kornievskaia }
438c975c209SOlga Kornievskaia 
439c975c209SOlga Kornievskaia static const struct rpc_call_ops nfs42_offload_cancel_ops = {
440c975c209SOlga Kornievskaia 	.rpc_call_prepare = nfs42_offload_cancel_prepare,
441c975c209SOlga Kornievskaia 	.rpc_call_done = nfs42_offload_cancel_done,
442c975c209SOlga Kornievskaia 	.rpc_release = nfs42_free_offloadcancel_data,
443c975c209SOlga Kornievskaia };
444c975c209SOlga Kornievskaia 
445c975c209SOlga Kornievskaia static int nfs42_do_offload_cancel_async(struct file *dst,
446c975c209SOlga Kornievskaia 					 nfs4_stateid *stateid)
447c975c209SOlga Kornievskaia {
448c975c209SOlga Kornievskaia 	struct nfs_server *dst_server = NFS_SERVER(file_inode(dst));
449c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = NULL;
450c975c209SOlga Kornievskaia 	struct nfs_open_context *ctx = nfs_file_open_context(dst);
451c975c209SOlga Kornievskaia 	struct rpc_task *task;
452c975c209SOlga Kornievskaia 	struct rpc_message msg = {
453c975c209SOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OFFLOAD_CANCEL],
454c975c209SOlga Kornievskaia 		.rpc_cred = ctx->cred,
455c975c209SOlga Kornievskaia 	};
456c975c209SOlga Kornievskaia 	struct rpc_task_setup task_setup_data = {
457c975c209SOlga Kornievskaia 		.rpc_client = dst_server->client,
458c975c209SOlga Kornievskaia 		.rpc_message = &msg,
459c975c209SOlga Kornievskaia 		.callback_ops = &nfs42_offload_cancel_ops,
460c975c209SOlga Kornievskaia 		.workqueue = nfsiod_workqueue,
461c975c209SOlga Kornievskaia 		.flags = RPC_TASK_ASYNC,
462c975c209SOlga Kornievskaia 	};
463c975c209SOlga Kornievskaia 	int status;
464c975c209SOlga Kornievskaia 
465c975c209SOlga Kornievskaia 	if (!(dst_server->caps & NFS_CAP_OFFLOAD_CANCEL))
466c975c209SOlga Kornievskaia 		return -EOPNOTSUPP;
467c975c209SOlga Kornievskaia 
468c975c209SOlga Kornievskaia 	data = kzalloc(sizeof(struct nfs42_offloadcancel_data), GFP_NOFS);
469c975c209SOlga Kornievskaia 	if (data == NULL)
470c975c209SOlga Kornievskaia 		return -ENOMEM;
471c975c209SOlga Kornievskaia 
472c975c209SOlga Kornievskaia 	data->seq_server = dst_server;
473c975c209SOlga Kornievskaia 	data->args.osa_src_fh = NFS_FH(file_inode(dst));
474c975c209SOlga Kornievskaia 	memcpy(&data->args.osa_stateid, stateid,
475c975c209SOlga Kornievskaia 		sizeof(data->args.osa_stateid));
476c975c209SOlga Kornievskaia 	msg.rpc_argp = &data->args;
477c975c209SOlga Kornievskaia 	msg.rpc_resp = &data->res;
478c975c209SOlga Kornievskaia 	task_setup_data.callback_data = data;
479c975c209SOlga Kornievskaia 	nfs4_init_sequence(&data->args.osa_seq_args, &data->res.osr_seq_res,
480c975c209SOlga Kornievskaia 			   1, 0);
481c975c209SOlga Kornievskaia 	task = rpc_run_task(&task_setup_data);
482c975c209SOlga Kornievskaia 	if (IS_ERR(task))
483c975c209SOlga Kornievskaia 		return PTR_ERR(task);
484c975c209SOlga Kornievskaia 	status = rpc_wait_for_completion_task(task);
485c975c209SOlga Kornievskaia 	if (status == -ENOTSUPP)
486c975c209SOlga Kornievskaia 		dst_server->caps &= ~NFS_CAP_OFFLOAD_CANCEL;
487c975c209SOlga Kornievskaia 	rpc_put_task(task);
488c975c209SOlga Kornievskaia 	return status;
489c975c209SOlga Kornievskaia }
490c975c209SOlga Kornievskaia 
4910491567bSOlga Kornievskaia int _nfs42_proc_copy_notify(struct file *src, struct file *dst,
4920491567bSOlga Kornievskaia 			    struct nfs42_copy_notify_args *args,
4930491567bSOlga Kornievskaia 			    struct nfs42_copy_notify_res *res)
4940491567bSOlga Kornievskaia {
4950491567bSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
4960491567bSOlga Kornievskaia 	struct rpc_message msg = {
4970491567bSOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY_NOTIFY],
4980491567bSOlga Kornievskaia 		.rpc_argp = args,
4990491567bSOlga Kornievskaia 		.rpc_resp = res,
5000491567bSOlga Kornievskaia 	};
5010491567bSOlga Kornievskaia 	int status;
5020491567bSOlga Kornievskaia 	struct nfs_open_context *ctx;
5030491567bSOlga Kornievskaia 	struct nfs_lock_context *l_ctx;
5040491567bSOlga Kornievskaia 
5050491567bSOlga Kornievskaia 	ctx = get_nfs_open_context(nfs_file_open_context(src));
5060491567bSOlga Kornievskaia 	l_ctx = nfs_get_lock_context(ctx);
5070491567bSOlga Kornievskaia 	if (IS_ERR(l_ctx))
5080491567bSOlga Kornievskaia 		return PTR_ERR(l_ctx);
5090491567bSOlga Kornievskaia 
5100491567bSOlga Kornievskaia 	status = nfs4_set_rw_stateid(&args->cna_src_stateid, ctx, l_ctx,
5110491567bSOlga Kornievskaia 				     FMODE_READ);
5120491567bSOlga Kornievskaia 	nfs_put_lock_context(l_ctx);
5130491567bSOlga Kornievskaia 	if (status)
5140491567bSOlga Kornievskaia 		return status;
5150491567bSOlga Kornievskaia 
5160491567bSOlga Kornievskaia 	status = nfs4_call_sync(src_server->client, src_server, &msg,
5170491567bSOlga Kornievskaia 				&args->cna_seq_args, &res->cnr_seq_res, 0);
5180491567bSOlga Kornievskaia 	if (status == -ENOTSUPP)
5190491567bSOlga Kornievskaia 		src_server->caps &= ~NFS_CAP_COPY_NOTIFY;
5200491567bSOlga Kornievskaia 
5210491567bSOlga Kornievskaia 	put_nfs_open_context(nfs_file_open_context(src));
5220491567bSOlga Kornievskaia 	return status;
5230491567bSOlga Kornievskaia }
5240491567bSOlga Kornievskaia 
5250491567bSOlga Kornievskaia int nfs42_proc_copy_notify(struct file *src, struct file *dst,
5260491567bSOlga Kornievskaia 				struct nfs42_copy_notify_res *res)
5270491567bSOlga Kornievskaia {
5280491567bSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
5290491567bSOlga Kornievskaia 	struct nfs42_copy_notify_args *args;
5300491567bSOlga Kornievskaia 	struct nfs4_exception exception = {
5310491567bSOlga Kornievskaia 		.inode = file_inode(src),
5320491567bSOlga Kornievskaia 	};
5330491567bSOlga Kornievskaia 	int status;
5340491567bSOlga Kornievskaia 
5350491567bSOlga Kornievskaia 	if (!(src_server->caps & NFS_CAP_COPY_NOTIFY))
5360491567bSOlga Kornievskaia 		return -EOPNOTSUPP;
5370491567bSOlga Kornievskaia 
5380491567bSOlga Kornievskaia 	args = kzalloc(sizeof(struct nfs42_copy_notify_args), GFP_NOFS);
5390491567bSOlga Kornievskaia 	if (args == NULL)
5400491567bSOlga Kornievskaia 		return -ENOMEM;
5410491567bSOlga Kornievskaia 
5420491567bSOlga Kornievskaia 	args->cna_src_fh  = NFS_FH(file_inode(src)),
5430491567bSOlga Kornievskaia 	args->cna_dst.nl4_type = NL4_NETADDR;
5440491567bSOlga Kornievskaia 	nfs42_set_netaddr(dst, &args->cna_dst.u.nl4_addr);
5450491567bSOlga Kornievskaia 	exception.stateid = &args->cna_src_stateid;
5460491567bSOlga Kornievskaia 
5470491567bSOlga Kornievskaia 	do {
5480491567bSOlga Kornievskaia 		status = _nfs42_proc_copy_notify(src, dst, args, res);
5490491567bSOlga Kornievskaia 		if (status == -ENOTSUPP) {
5500491567bSOlga Kornievskaia 			status = -EOPNOTSUPP;
5510491567bSOlga Kornievskaia 			goto out;
5520491567bSOlga Kornievskaia 		}
5530491567bSOlga Kornievskaia 		status = nfs4_handle_exception(src_server, status, &exception);
5540491567bSOlga Kornievskaia 	} while (exception.retry);
5550491567bSOlga Kornievskaia 
5560491567bSOlga Kornievskaia out:
5570491567bSOlga Kornievskaia 	kfree(args);
5580491567bSOlga Kornievskaia 	return status;
5590491567bSOlga Kornievskaia }
5600491567bSOlga Kornievskaia 
5614bdf87ebSChristoph Hellwig static loff_t _nfs42_proc_llseek(struct file *filep,
5624bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, int whence)
5631c6dcbe5SAnna Schumaker {
5641c6dcbe5SAnna Schumaker 	struct inode *inode = file_inode(filep);
5651c6dcbe5SAnna Schumaker 	struct nfs42_seek_args args = {
5661c6dcbe5SAnna Schumaker 		.sa_fh		= NFS_FH(inode),
5671c6dcbe5SAnna Schumaker 		.sa_offset	= offset,
5681c6dcbe5SAnna Schumaker 		.sa_what	= (whence == SEEK_HOLE) ?
5691c6dcbe5SAnna Schumaker 					NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA,
5701c6dcbe5SAnna Schumaker 	};
5711c6dcbe5SAnna Schumaker 	struct nfs42_seek_res res;
5721c6dcbe5SAnna Schumaker 	struct rpc_message msg = {
5731c6dcbe5SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK],
5741c6dcbe5SAnna Schumaker 		.rpc_argp = &args,
5751c6dcbe5SAnna Schumaker 		.rpc_resp = &res,
5761c6dcbe5SAnna Schumaker 	};
5771c6dcbe5SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
5781c6dcbe5SAnna Schumaker 	int status;
5791c6dcbe5SAnna Schumaker 
580878ffa9fSAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_SEEK))
5811c6dcbe5SAnna Schumaker 		return -ENOTSUPP;
5821c6dcbe5SAnna Schumaker 
5834bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context,
5844bdf87ebSChristoph Hellwig 			lock, FMODE_READ);
5851c6dcbe5SAnna Schumaker 	if (status)
5861c6dcbe5SAnna Schumaker 		return status;
5871c6dcbe5SAnna Schumaker 
588e95fc4a0STrond Myklebust 	status = nfs_filemap_write_and_wait_range(inode->i_mapping,
589e95fc4a0STrond Myklebust 			offset, LLONG_MAX);
590e95fc4a0STrond Myklebust 	if (status)
591e95fc4a0STrond Myklebust 		return status;
592e95fc4a0STrond Myklebust 
5931c6dcbe5SAnna Schumaker 	status = nfs4_call_sync(server->client, server, &msg,
5941c6dcbe5SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
5951c6dcbe5SAnna Schumaker 	if (status == -ENOTSUPP)
5961c6dcbe5SAnna Schumaker 		server->caps &= ~NFS_CAP_SEEK;
5971c6dcbe5SAnna Schumaker 	if (status)
5981c6dcbe5SAnna Schumaker 		return status;
5991c6dcbe5SAnna Schumaker 
6001c6dcbe5SAnna Schumaker 	return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes);
6011c6dcbe5SAnna Schumaker }
602be3a5d23STrond Myklebust 
603bdcc2cd1SJ. Bruce Fields loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
604bdcc2cd1SJ. Bruce Fields {
605bdcc2cd1SJ. Bruce Fields 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
606bdcc2cd1SJ. Bruce Fields 	struct nfs4_exception exception = { };
6074bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
608306a5549SJ. Bruce Fields 	loff_t err;
609bdcc2cd1SJ. Bruce Fields 
6104bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
6114bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
6124bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
6134bdf87ebSChristoph Hellwig 
6144bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
6154bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
6164bdf87ebSChristoph Hellwig 
617bdcc2cd1SJ. Bruce Fields 	do {
6184bdf87ebSChristoph Hellwig 		err = _nfs42_proc_llseek(filep, lock, offset, whence);
619306a5549SJ. Bruce Fields 		if (err >= 0)
620306a5549SJ. Bruce Fields 			break;
6214bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
6224bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
6234bdf87ebSChristoph Hellwig 			break;
6244bdf87ebSChristoph Hellwig 		}
625bdcc2cd1SJ. Bruce Fields 		err = nfs4_handle_exception(server, err, &exception);
626bdcc2cd1SJ. Bruce Fields 	} while (exception.retry);
627bdcc2cd1SJ. Bruce Fields 
6284bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
629bdcc2cd1SJ. Bruce Fields 	return err;
630bdcc2cd1SJ. Bruce Fields }
631bdcc2cd1SJ. Bruce Fields 
632bdcc2cd1SJ. Bruce Fields 
6331b4a4bd8SPeng Tao static void
6341b4a4bd8SPeng Tao nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata)
6351b4a4bd8SPeng Tao {
6361b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
6379a0fe867STrond Myklebust 	struct inode *inode = data->inode;
6389a0fe867STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
6399a0fe867STrond Myklebust 	struct pnfs_layout_hdr *lo;
6401b4a4bd8SPeng Tao 
6419a0fe867STrond Myklebust 	spin_lock(&inode->i_lock);
6429a0fe867STrond Myklebust 	lo = NFS_I(inode)->layout;
6439a0fe867STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
6449a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
6459a0fe867STrond Myklebust 		rpc_exit(task, 0);
6469a0fe867STrond Myklebust 		return;
6479a0fe867STrond Myklebust 	}
6489a0fe867STrond Myklebust 	nfs4_stateid_copy(&data->args.stateid, &lo->plh_stateid);
6499a0fe867STrond Myklebust 	spin_unlock(&inode->i_lock);
6506de7e12fSAnna Schumaker 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
6516de7e12fSAnna Schumaker 			    &data->res.seq_res, task);
6521b4a4bd8SPeng Tao }
6531b4a4bd8SPeng Tao 
6541b4a4bd8SPeng Tao static void
6551b4a4bd8SPeng Tao nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
6561b4a4bd8SPeng Tao {
6571b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
65868d264cfSPeng Tao 	struct inode *inode = data->inode;
65968d264cfSPeng Tao 	struct pnfs_layout_hdr *lo;
6601b4a4bd8SPeng Tao 
6611b4a4bd8SPeng Tao 	if (!nfs4_sequence_done(task, &data->res.seq_res))
6621b4a4bd8SPeng Tao 		return;
6631b4a4bd8SPeng Tao 
6646c5a0d89STrond Myklebust 	switch (task->tk_status) {
6656c5a0d89STrond Myklebust 	case 0:
6666c5a0d89STrond Myklebust 		break;
667cf61eb26STrond Myklebust 	case -NFS4ERR_BADHANDLE:
668cf61eb26STrond Myklebust 	case -ESTALE:
669cf61eb26STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
670cf61eb26STrond Myklebust 		break;
67168d264cfSPeng Tao 	case -NFS4ERR_EXPIRED:
672206b3bb5STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
673206b3bb5STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
67468d264cfSPeng Tao 	case -NFS4ERR_STALE_STATEID:
67568d264cfSPeng Tao 	case -NFS4ERR_BAD_STATEID:
67668d264cfSPeng Tao 		spin_lock(&inode->i_lock);
67768d264cfSPeng Tao 		lo = NFS_I(inode)->layout;
6789a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
6799a0fe867STrond Myklebust 		    nfs4_stateid_match(&data->args.stateid,
68068d264cfSPeng Tao 					     &lo->plh_stateid)) {
68168d264cfSPeng Tao 			LIST_HEAD(head);
68268d264cfSPeng Tao 
68368d264cfSPeng Tao 			/*
68468d264cfSPeng Tao 			 * Mark the bad layout state as invalid, then retry
68568d264cfSPeng Tao 			 * with the current stateid.
68668d264cfSPeng Tao 			 */
6875f46be04STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
68868d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
68968d264cfSPeng Tao 			pnfs_free_lseg_list(&head);
6901f18b82cSTrond Myklebust 			nfs_commit_inode(inode, 0);
69168d264cfSPeng Tao 		} else
69268d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
69368d264cfSPeng Tao 		break;
6949a0fe867STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
6959a0fe867STrond Myklebust 		spin_lock(&inode->i_lock);
6969a0fe867STrond Myklebust 		lo = NFS_I(inode)->layout;
6979a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
6989a0fe867STrond Myklebust 		    nfs4_stateid_match_other(&data->args.stateid,
6999a0fe867STrond Myklebust 					&lo->plh_stateid)) {
7009a0fe867STrond Myklebust 			/* Do we need to delay before resending? */
7019a0fe867STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
7029a0fe867STrond Myklebust 						&data->args.stateid))
7039a0fe867STrond Myklebust 				rpc_delay(task, HZ);
7049a0fe867STrond Myklebust 			rpc_restart_call_prepare(task);
7059a0fe867STrond Myklebust 		}
7069a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
7079a0fe867STrond Myklebust 		break;
7086c5a0d89STrond Myklebust 	case -ENOTSUPP:
7096c5a0d89STrond Myklebust 	case -EOPNOTSUPP:
71068d264cfSPeng Tao 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
7111b4a4bd8SPeng Tao 	}
7126c5a0d89STrond Myklebust }
7131b4a4bd8SPeng Tao 
7141b4a4bd8SPeng Tao static void
7151b4a4bd8SPeng Tao nfs42_layoutstat_release(void *calldata)
7161b4a4bd8SPeng Tao {
7171b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
718422c93c8STrond Myklebust 	struct nfs42_layoutstat_devinfo *devinfo = data->args.devinfo;
719422c93c8STrond Myklebust 	int i;
7208733408dSPeng Tao 
721422c93c8STrond Myklebust 	for (i = 0; i < data->args.num_dev; i++) {
722422c93c8STrond Myklebust 		if (devinfo[i].ld_private.ops && devinfo[i].ld_private.ops->free)
723422c93c8STrond Myklebust 			devinfo[i].ld_private.ops->free(&devinfo[i].ld_private);
724422c93c8STrond Myklebust 	}
7251b4a4bd8SPeng Tao 
7261b4a4bd8SPeng Tao 	pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout);
7271bfe3b25SPeng Tao 	smp_mb__before_atomic();
7281bfe3b25SPeng Tao 	clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags);
7291bfe3b25SPeng Tao 	smp_mb__after_atomic();
7301b4a4bd8SPeng Tao 	nfs_iput_and_deactive(data->inode);
7311b4a4bd8SPeng Tao 	kfree(data->args.devinfo);
7321b4a4bd8SPeng Tao 	kfree(data);
7331b4a4bd8SPeng Tao }
7341b4a4bd8SPeng Tao 
735be3a5d23STrond Myklebust static const struct rpc_call_ops nfs42_layoutstat_ops = {
7361b4a4bd8SPeng Tao 	.rpc_call_prepare = nfs42_layoutstat_prepare,
7371b4a4bd8SPeng Tao 	.rpc_call_done = nfs42_layoutstat_done,
7381b4a4bd8SPeng Tao 	.rpc_release = nfs42_layoutstat_release,
739be3a5d23STrond Myklebust };
740be3a5d23STrond Myklebust 
741be3a5d23STrond Myklebust int nfs42_proc_layoutstats_generic(struct nfs_server *server,
742be3a5d23STrond Myklebust 				   struct nfs42_layoutstat_data *data)
743be3a5d23STrond Myklebust {
744be3a5d23STrond Myklebust 	struct rpc_message msg = {
745be3a5d23STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS],
746be3a5d23STrond Myklebust 		.rpc_argp = &data->args,
747be3a5d23STrond Myklebust 		.rpc_resp = &data->res,
748be3a5d23STrond Myklebust 	};
749be3a5d23STrond Myklebust 	struct rpc_task_setup task_setup = {
750be3a5d23STrond Myklebust 		.rpc_client = server->client,
751be3a5d23STrond Myklebust 		.rpc_message = &msg,
752be3a5d23STrond Myklebust 		.callback_ops = &nfs42_layoutstat_ops,
753be3a5d23STrond Myklebust 		.callback_data = data,
754be3a5d23STrond Myklebust 		.flags = RPC_TASK_ASYNC,
755be3a5d23STrond Myklebust 	};
756be3a5d23STrond Myklebust 	struct rpc_task *task;
757be3a5d23STrond Myklebust 
7581b4a4bd8SPeng Tao 	data->inode = nfs_igrab_and_active(data->args.inode);
7591b4a4bd8SPeng Tao 	if (!data->inode) {
7601b4a4bd8SPeng Tao 		nfs42_layoutstat_release(data);
7611b4a4bd8SPeng Tao 		return -EAGAIN;
7621b4a4bd8SPeng Tao 	}
763fba83f34SAnna Schumaker 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
764be3a5d23STrond Myklebust 	task = rpc_run_task(&task_setup);
765be3a5d23STrond Myklebust 	if (IS_ERR(task))
766be3a5d23STrond Myklebust 		return PTR_ERR(task);
7673f807e5aSJeff Layton 	rpc_put_task(task);
768be3a5d23STrond Myklebust 	return 0;
769be3a5d23STrond Myklebust }
770e5341f3aSPeng Tao 
7713eb86093STrond Myklebust static struct nfs42_layouterror_data *
7723eb86093STrond Myklebust nfs42_alloc_layouterror_data(struct pnfs_layout_segment *lseg, gfp_t gfp_flags)
7733eb86093STrond Myklebust {
7743eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
7753eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
7763eb86093STrond Myklebust 
7773eb86093STrond Myklebust 	data = kzalloc(sizeof(*data), gfp_flags);
7783eb86093STrond Myklebust 	if (data) {
7793eb86093STrond Myklebust 		data->args.inode = data->inode = nfs_igrab_and_active(inode);
7803eb86093STrond Myklebust 		if (data->inode) {
7813eb86093STrond Myklebust 			data->lseg = pnfs_get_lseg(lseg);
7823eb86093STrond Myklebust 			if (data->lseg)
7833eb86093STrond Myklebust 				return data;
7843eb86093STrond Myklebust 			nfs_iput_and_deactive(data->inode);
7853eb86093STrond Myklebust 		}
7863eb86093STrond Myklebust 		kfree(data);
7873eb86093STrond Myklebust 	}
7883eb86093STrond Myklebust 	return NULL;
7893eb86093STrond Myklebust }
7903eb86093STrond Myklebust 
7913eb86093STrond Myklebust static void
7923eb86093STrond Myklebust nfs42_free_layouterror_data(struct nfs42_layouterror_data *data)
7933eb86093STrond Myklebust {
7943eb86093STrond Myklebust 	pnfs_put_lseg(data->lseg);
7953eb86093STrond Myklebust 	nfs_iput_and_deactive(data->inode);
7963eb86093STrond Myklebust 	kfree(data);
7973eb86093STrond Myklebust }
7983eb86093STrond Myklebust 
7993eb86093STrond Myklebust static void
8003eb86093STrond Myklebust nfs42_layouterror_prepare(struct rpc_task *task, void *calldata)
8013eb86093STrond Myklebust {
8023eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
8033eb86093STrond Myklebust 	struct inode *inode = data->inode;
8043eb86093STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
8053eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
8063eb86093STrond Myklebust 	unsigned i;
8073eb86093STrond Myklebust 
8083eb86093STrond Myklebust 	spin_lock(&inode->i_lock);
8093eb86093STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
8103eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
8113eb86093STrond Myklebust 		rpc_exit(task, 0);
8123eb86093STrond Myklebust 		return;
8133eb86093STrond Myklebust 	}
8143eb86093STrond Myklebust 	for (i = 0; i < data->args.num_errors; i++)
8153eb86093STrond Myklebust 		nfs4_stateid_copy(&data->args.errors[i].stateid,
8163eb86093STrond Myklebust 				&lo->plh_stateid);
8173eb86093STrond Myklebust 	spin_unlock(&inode->i_lock);
8183eb86093STrond Myklebust 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
8193eb86093STrond Myklebust 			    &data->res.seq_res, task);
8203eb86093STrond Myklebust }
8213eb86093STrond Myklebust 
8223eb86093STrond Myklebust static void
8233eb86093STrond Myklebust nfs42_layouterror_done(struct rpc_task *task, void *calldata)
8243eb86093STrond Myklebust {
8253eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
8263eb86093STrond Myklebust 	struct inode *inode = data->inode;
8273eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
8283eb86093STrond Myklebust 
8293eb86093STrond Myklebust 	if (!nfs4_sequence_done(task, &data->res.seq_res))
8303eb86093STrond Myklebust 		return;
8313eb86093STrond Myklebust 
8323eb86093STrond Myklebust 	switch (task->tk_status) {
8333eb86093STrond Myklebust 	case 0:
8343eb86093STrond Myklebust 		break;
8353eb86093STrond Myklebust 	case -NFS4ERR_BADHANDLE:
8363eb86093STrond Myklebust 	case -ESTALE:
8373eb86093STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
8383eb86093STrond Myklebust 		break;
8393eb86093STrond Myklebust 	case -NFS4ERR_EXPIRED:
8403eb86093STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
8413eb86093STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
8423eb86093STrond Myklebust 	case -NFS4ERR_STALE_STATEID:
8433eb86093STrond Myklebust 	case -NFS4ERR_BAD_STATEID:
8443eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
8453eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
8463eb86093STrond Myklebust 		    nfs4_stateid_match(&data->args.errors[0].stateid,
8473eb86093STrond Myklebust 					     &lo->plh_stateid)) {
8483eb86093STrond Myklebust 			LIST_HEAD(head);
8493eb86093STrond Myklebust 
8503eb86093STrond Myklebust 			/*
8513eb86093STrond Myklebust 			 * Mark the bad layout state as invalid, then retry
8523eb86093STrond Myklebust 			 * with the current stateid.
8533eb86093STrond Myklebust 			 */
8543eb86093STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
8553eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
8563eb86093STrond Myklebust 			pnfs_free_lseg_list(&head);
8573eb86093STrond Myklebust 			nfs_commit_inode(inode, 0);
8583eb86093STrond Myklebust 		} else
8593eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
8603eb86093STrond Myklebust 		break;
8613eb86093STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
8623eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
8633eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
8643eb86093STrond Myklebust 		    nfs4_stateid_match_other(&data->args.errors[0].stateid,
8653eb86093STrond Myklebust 					&lo->plh_stateid)) {
8663eb86093STrond Myklebust 			/* Do we need to delay before resending? */
8673eb86093STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
8683eb86093STrond Myklebust 						&data->args.errors[0].stateid))
8693eb86093STrond Myklebust 				rpc_delay(task, HZ);
8703eb86093STrond Myklebust 			rpc_restart_call_prepare(task);
8713eb86093STrond Myklebust 		}
8723eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
8733eb86093STrond Myklebust 		break;
8743eb86093STrond Myklebust 	case -ENOTSUPP:
8753eb86093STrond Myklebust 	case -EOPNOTSUPP:
8763eb86093STrond Myklebust 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTERROR;
8773eb86093STrond Myklebust 	}
8783eb86093STrond Myklebust }
8793eb86093STrond Myklebust 
8803eb86093STrond Myklebust static void
8813eb86093STrond Myklebust nfs42_layouterror_release(void *calldata)
8823eb86093STrond Myklebust {
8833eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
8843eb86093STrond Myklebust 
8853eb86093STrond Myklebust 	nfs42_free_layouterror_data(data);
8863eb86093STrond Myklebust }
8873eb86093STrond Myklebust 
8883eb86093STrond Myklebust static const struct rpc_call_ops nfs42_layouterror_ops = {
8893eb86093STrond Myklebust 	.rpc_call_prepare = nfs42_layouterror_prepare,
8903eb86093STrond Myklebust 	.rpc_call_done = nfs42_layouterror_done,
8913eb86093STrond Myklebust 	.rpc_release = nfs42_layouterror_release,
8923eb86093STrond Myklebust };
8933eb86093STrond Myklebust 
8943eb86093STrond Myklebust int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
8953eb86093STrond Myklebust 		const struct nfs42_layout_error *errors, size_t n)
8963eb86093STrond Myklebust {
8973eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
8983eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
8993eb86093STrond Myklebust 	struct rpc_task *task;
9003eb86093STrond Myklebust 	struct rpc_message msg = {
9013eb86093STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTERROR],
9023eb86093STrond Myklebust 	};
9033eb86093STrond Myklebust 	struct rpc_task_setup task_setup = {
9043eb86093STrond Myklebust 		.rpc_message = &msg,
9053eb86093STrond Myklebust 		.callback_ops = &nfs42_layouterror_ops,
9063eb86093STrond Myklebust 		.flags = RPC_TASK_ASYNC,
9073eb86093STrond Myklebust 	};
9083eb86093STrond Myklebust 	unsigned int i;
9093eb86093STrond Myklebust 
9103eb86093STrond Myklebust 	if (!nfs_server_capable(inode, NFS_CAP_LAYOUTERROR))
9113eb86093STrond Myklebust 		return -EOPNOTSUPP;
9123eb86093STrond Myklebust 	if (n > NFS42_LAYOUTERROR_MAX)
9133eb86093STrond Myklebust 		return -EINVAL;
9143eb86093STrond Myklebust 	data = nfs42_alloc_layouterror_data(lseg, GFP_NOFS);
9153eb86093STrond Myklebust 	if (!data)
9163eb86093STrond Myklebust 		return -ENOMEM;
9173eb86093STrond Myklebust 	for (i = 0; i < n; i++) {
9183eb86093STrond Myklebust 		data->args.errors[i] = errors[i];
9193eb86093STrond Myklebust 		data->args.num_errors++;
9203eb86093STrond Myklebust 		data->res.num_errors++;
9213eb86093STrond Myklebust 	}
9223eb86093STrond Myklebust 	msg.rpc_argp = &data->args;
9233eb86093STrond Myklebust 	msg.rpc_resp = &data->res;
9243eb86093STrond Myklebust 	task_setup.callback_data = data;
9253eb86093STrond Myklebust 	task_setup.rpc_client = NFS_SERVER(inode)->client;
9263eb86093STrond Myklebust 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
9273eb86093STrond Myklebust 	task = rpc_run_task(&task_setup);
9283eb86093STrond Myklebust 	if (IS_ERR(task))
9293eb86093STrond Myklebust 		return PTR_ERR(task);
9303eb86093STrond Myklebust 	rpc_put_task(task);
9313eb86093STrond Myklebust 	return 0;
9323eb86093STrond Myklebust }
9333eb86093STrond Myklebust EXPORT_SYMBOL_GPL(nfs42_proc_layouterror);
9343eb86093STrond Myklebust 
935e5341f3aSPeng Tao static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
9364bdf87ebSChristoph Hellwig 		struct file *dst_f, struct nfs_lock_context *src_lock,
9374bdf87ebSChristoph Hellwig 		struct nfs_lock_context *dst_lock, loff_t src_offset,
938e5341f3aSPeng Tao 		loff_t dst_offset, loff_t count)
939e5341f3aSPeng Tao {
940e5341f3aSPeng Tao 	struct inode *src_inode = file_inode(src_f);
941e5341f3aSPeng Tao 	struct inode *dst_inode = file_inode(dst_f);
942e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(dst_inode);
943e5341f3aSPeng Tao 	struct nfs42_clone_args args = {
944e5341f3aSPeng Tao 		.src_fh = NFS_FH(src_inode),
945e5341f3aSPeng Tao 		.dst_fh = NFS_FH(dst_inode),
946e5341f3aSPeng Tao 		.src_offset = src_offset,
947e5341f3aSPeng Tao 		.dst_offset = dst_offset,
9489494b2ceSChristoph Hellwig 		.count = count,
949e5341f3aSPeng Tao 		.dst_bitmask = server->cache_consistency_bitmask,
950e5341f3aSPeng Tao 	};
951e5341f3aSPeng Tao 	struct nfs42_clone_res res = {
952e5341f3aSPeng Tao 		.server	= server,
953e5341f3aSPeng Tao 	};
954e5341f3aSPeng Tao 	int status;
955e5341f3aSPeng Tao 
956e5341f3aSPeng Tao 	msg->rpc_argp = &args;
957e5341f3aSPeng Tao 	msg->rpc_resp = &res;
958e5341f3aSPeng Tao 
9594bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context,
9604bdf87ebSChristoph Hellwig 			src_lock, FMODE_READ);
961e5341f3aSPeng Tao 	if (status)
962e5341f3aSPeng Tao 		return status;
963e5341f3aSPeng Tao 
9644bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
9654bdf87ebSChristoph Hellwig 			dst_lock, FMODE_WRITE);
966e5341f3aSPeng Tao 	if (status)
967e5341f3aSPeng Tao 		return status;
968e5341f3aSPeng Tao 
969e5341f3aSPeng Tao 	res.dst_fattr = nfs_alloc_fattr();
970e5341f3aSPeng Tao 	if (!res.dst_fattr)
971e5341f3aSPeng Tao 		return -ENOMEM;
972e5341f3aSPeng Tao 
973e5341f3aSPeng Tao 	status = nfs4_call_sync(server->client, server, msg,
974e5341f3aSPeng Tao 				&args.seq_args, &res.seq_res, 0);
975e5341f3aSPeng Tao 	if (status == 0)
976e5341f3aSPeng Tao 		status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
977e5341f3aSPeng Tao 
978e5341f3aSPeng Tao 	kfree(res.dst_fattr);
979e5341f3aSPeng Tao 	return status;
980e5341f3aSPeng Tao }
981e5341f3aSPeng Tao 
982e5341f3aSPeng Tao int nfs42_proc_clone(struct file *src_f, struct file *dst_f,
983e5341f3aSPeng Tao 		     loff_t src_offset, loff_t dst_offset, loff_t count)
984e5341f3aSPeng Tao {
985e5341f3aSPeng Tao 	struct rpc_message msg = {
986e5341f3aSPeng Tao 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE],
987e5341f3aSPeng Tao 	};
988e5341f3aSPeng Tao 	struct inode *inode = file_inode(src_f);
989e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(file_inode(src_f));
9904bdf87ebSChristoph Hellwig 	struct nfs_lock_context *src_lock;
9914bdf87ebSChristoph Hellwig 	struct nfs_lock_context *dst_lock;
9924bdf87ebSChristoph Hellwig 	struct nfs4_exception src_exception = { };
9934bdf87ebSChristoph Hellwig 	struct nfs4_exception dst_exception = { };
9944bdf87ebSChristoph Hellwig 	int err, err2;
995e5341f3aSPeng Tao 
996e5341f3aSPeng Tao 	if (!nfs_server_capable(inode, NFS_CAP_CLONE))
997e5341f3aSPeng Tao 		return -EOPNOTSUPP;
998e5341f3aSPeng Tao 
9994bdf87ebSChristoph Hellwig 	src_lock = nfs_get_lock_context(nfs_file_open_context(src_f));
10004bdf87ebSChristoph Hellwig 	if (IS_ERR(src_lock))
10014bdf87ebSChristoph Hellwig 		return PTR_ERR(src_lock);
10024bdf87ebSChristoph Hellwig 
10034bdf87ebSChristoph Hellwig 	src_exception.inode = file_inode(src_f);
10044bdf87ebSChristoph Hellwig 	src_exception.state = src_lock->open_context->state;
10054bdf87ebSChristoph Hellwig 
10064bdf87ebSChristoph Hellwig 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f));
10074bdf87ebSChristoph Hellwig 	if (IS_ERR(dst_lock)) {
10084bdf87ebSChristoph Hellwig 		err = PTR_ERR(dst_lock);
10094bdf87ebSChristoph Hellwig 		goto out_put_src_lock;
10104bdf87ebSChristoph Hellwig 	}
10114bdf87ebSChristoph Hellwig 
10124bdf87ebSChristoph Hellwig 	dst_exception.inode = file_inode(dst_f);
10134bdf87ebSChristoph Hellwig 	dst_exception.state = dst_lock->open_context->state;
10144bdf87ebSChristoph Hellwig 
1015e5341f3aSPeng Tao 	do {
10164bdf87ebSChristoph Hellwig 		err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock,
10174bdf87ebSChristoph Hellwig 					src_offset, dst_offset, count);
1018e5341f3aSPeng Tao 		if (err == -ENOTSUPP || err == -EOPNOTSUPP) {
1019e5341f3aSPeng Tao 			NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE;
10204bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
10214bdf87ebSChristoph Hellwig 			break;
1022e5341f3aSPeng Tao 		}
1023e5341f3aSPeng Tao 
10244bdf87ebSChristoph Hellwig 		err2 = nfs4_handle_exception(server, err, &src_exception);
10254bdf87ebSChristoph Hellwig 		err = nfs4_handle_exception(server, err, &dst_exception);
10264bdf87ebSChristoph Hellwig 		if (!err)
10274bdf87ebSChristoph Hellwig 			err = err2;
10284bdf87ebSChristoph Hellwig 	} while (src_exception.retry || dst_exception.retry);
10294bdf87ebSChristoph Hellwig 
10304bdf87ebSChristoph Hellwig 	nfs_put_lock_context(dst_lock);
10314bdf87ebSChristoph Hellwig out_put_src_lock:
10324bdf87ebSChristoph Hellwig 	nfs_put_lock_context(src_lock);
1033e5341f3aSPeng Tao 	return err;
1034e5341f3aSPeng Tao }
1035