xref: /openbmc/linux/fs/nfs/nfs42proc.c (revision 913eca1a)
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,
52913eca1aSAnna Schumaker 		.falloc_bitmask	= nfs4_fattr_bitmap,
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,
1560e65a32cSOlga Kornievskaia 			     struct nfs_server *dst_server,
1570e65a32cSOlga Kornievskaia 			     struct nfs_server *src_server,
15862164f31SOlga Kornievskaia 			     struct file *src,
15962164f31SOlga Kornievskaia 			     struct file *dst,
1600e65a32cSOlga Kornievskaia 			     nfs4_stateid *src_stateid,
1610e65a32cSOlga Kornievskaia 			     bool *restart)
16262164f31SOlga Kornievskaia {
16399f2c555SOlga Kornievskaia 	struct nfs4_copy_state *copy, *tmp_copy;
16462164f31SOlga Kornievskaia 	int status = NFS4_OK;
165bc0c9079SOlga Kornievskaia 	bool found_pending = false;
1660e65a32cSOlga Kornievskaia 	struct nfs_open_context *dst_ctx = nfs_file_open_context(dst);
1670e65a32cSOlga Kornievskaia 	struct nfs_open_context *src_ctx = nfs_file_open_context(src);
168bc0c9079SOlga Kornievskaia 
16999f2c555SOlga Kornievskaia 	copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
17099f2c555SOlga Kornievskaia 	if (!copy)
17199f2c555SOlga Kornievskaia 		return -ENOMEM;
17299f2c555SOlga Kornievskaia 
1730e65a32cSOlga Kornievskaia 	spin_lock(&dst_server->nfs_client->cl_lock);
1740e65a32cSOlga Kornievskaia 	list_for_each_entry(tmp_copy,
1750e65a32cSOlga Kornievskaia 				&dst_server->nfs_client->pending_cb_stateids,
176bc0c9079SOlga Kornievskaia 				copies) {
17799f2c555SOlga Kornievskaia 		if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
178bc0c9079SOlga Kornievskaia 				NFS4_STATEID_SIZE))
179bc0c9079SOlga Kornievskaia 			continue;
180bc0c9079SOlga Kornievskaia 		found_pending = true;
18199f2c555SOlga Kornievskaia 		list_del(&tmp_copy->copies);
182bc0c9079SOlga Kornievskaia 		break;
183bc0c9079SOlga Kornievskaia 	}
184bc0c9079SOlga Kornievskaia 	if (found_pending) {
1850e65a32cSOlga Kornievskaia 		spin_unlock(&dst_server->nfs_client->cl_lock);
18699f2c555SOlga Kornievskaia 		kfree(copy);
18799f2c555SOlga Kornievskaia 		copy = tmp_copy;
188bc0c9079SOlga Kornievskaia 		goto out;
189bc0c9079SOlga Kornievskaia 	}
19062164f31SOlga Kornievskaia 
19162164f31SOlga Kornievskaia 	memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
19262164f31SOlga Kornievskaia 	init_completion(&copy->completion);
1930e65a32cSOlga Kornievskaia 	copy->parent_dst_state = dst_ctx->state;
1940e65a32cSOlga Kornievskaia 	copy->parent_src_state = src_ctx->state;
19562164f31SOlga Kornievskaia 
1960e65a32cSOlga Kornievskaia 	list_add_tail(&copy->copies, &dst_server->ss_copies);
1970e65a32cSOlga Kornievskaia 	spin_unlock(&dst_server->nfs_client->cl_lock);
1980e65a32cSOlga Kornievskaia 
1990e65a32cSOlga Kornievskaia 	if (dst_server != src_server) {
2000e65a32cSOlga Kornievskaia 		spin_lock(&src_server->nfs_client->cl_lock);
2010e65a32cSOlga Kornievskaia 		list_add_tail(&copy->src_copies, &src_server->ss_copies);
2020e65a32cSOlga Kornievskaia 		spin_unlock(&src_server->nfs_client->cl_lock);
2030e65a32cSOlga Kornievskaia 	}
20462164f31SOlga Kornievskaia 
205c975c209SOlga Kornievskaia 	status = wait_for_completion_interruptible(&copy->completion);
2060e65a32cSOlga Kornievskaia 	spin_lock(&dst_server->nfs_client->cl_lock);
20762164f31SOlga Kornievskaia 	list_del_init(&copy->copies);
2080e65a32cSOlga Kornievskaia 	spin_unlock(&dst_server->nfs_client->cl_lock);
2090e65a32cSOlga Kornievskaia 	if (dst_server != src_server) {
2100e65a32cSOlga Kornievskaia 		spin_lock(&src_server->nfs_client->cl_lock);
2110e65a32cSOlga Kornievskaia 		list_del_init(&copy->src_copies);
2120e65a32cSOlga Kornievskaia 		spin_unlock(&src_server->nfs_client->cl_lock);
2130e65a32cSOlga Kornievskaia 	}
214c975c209SOlga Kornievskaia 	if (status == -ERESTARTSYS) {
215e4648aa4SOlga Kornievskaia 		goto out_cancel;
2160e65a32cSOlga Kornievskaia 	} else if (copy->flags || copy->error == NFS4ERR_PARTNER_NO_AUTH) {
217e4648aa4SOlga Kornievskaia 		status = -EAGAIN;
2180e65a32cSOlga Kornievskaia 		*restart = true;
219e4648aa4SOlga Kornievskaia 		goto out_cancel;
220c975c209SOlga Kornievskaia 	}
221bc0c9079SOlga Kornievskaia out:
22262164f31SOlga Kornievskaia 	res->write_res.count = copy->count;
22362164f31SOlga Kornievskaia 	memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
22462164f31SOlga Kornievskaia 	status = -copy->error;
22562164f31SOlga Kornievskaia 
22612406025SOlga Kornievskaia out_free:
22762164f31SOlga Kornievskaia 	kfree(copy);
22862164f31SOlga Kornievskaia 	return status;
229e4648aa4SOlga Kornievskaia out_cancel:
230e4648aa4SOlga Kornievskaia 	nfs42_do_offload_cancel_async(dst, &copy->stateid);
23112406025SOlga Kornievskaia 	if (!nfs42_files_from_same_server(src, dst))
23212406025SOlga Kornievskaia 		nfs42_do_offload_cancel_async(src, src_stateid);
23312406025SOlga Kornievskaia 	goto out_free;
23462164f31SOlga Kornievskaia }
23562164f31SOlga Kornievskaia 
2366b8d84e2SOlga Kornievskaia static int process_copy_commit(struct file *dst, loff_t pos_dst,
2376b8d84e2SOlga Kornievskaia 			       struct nfs42_copy_res *res)
2386b8d84e2SOlga Kornievskaia {
2396b8d84e2SOlga Kornievskaia 	struct nfs_commitres cres;
2406b8d84e2SOlga Kornievskaia 	int status = -ENOMEM;
2416b8d84e2SOlga Kornievskaia 
2426b8d84e2SOlga Kornievskaia 	cres.verf = kzalloc(sizeof(struct nfs_writeverf), GFP_NOFS);
2436b8d84e2SOlga Kornievskaia 	if (!cres.verf)
2446b8d84e2SOlga Kornievskaia 		goto out;
2456b8d84e2SOlga Kornievskaia 
2466b8d84e2SOlga Kornievskaia 	status = nfs4_proc_commit(dst, pos_dst, res->write_res.count, &cres);
2476b8d84e2SOlga Kornievskaia 	if (status)
2486b8d84e2SOlga Kornievskaia 		goto out_free;
2496b8d84e2SOlga Kornievskaia 	if (nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
2506b8d84e2SOlga Kornievskaia 				    &cres.verf->verifier)) {
2516b8d84e2SOlga Kornievskaia 		dprintk("commit verf differs from copy verf\n");
2526b8d84e2SOlga Kornievskaia 		status = -EAGAIN;
2536b8d84e2SOlga Kornievskaia 	}
2546b8d84e2SOlga Kornievskaia out_free:
2556b8d84e2SOlga Kornievskaia 	kfree(cres.verf);
2566b8d84e2SOlga Kornievskaia out:
2576b8d84e2SOlga Kornievskaia 	return status;
2586b8d84e2SOlga Kornievskaia }
2596b8d84e2SOlga Kornievskaia 
2609d8cacbfSTrond Myklebust static ssize_t _nfs42_proc_copy(struct file *src,
2612e72448bSAnna Schumaker 				struct nfs_lock_context *src_lock,
2629d8cacbfSTrond Myklebust 				struct file *dst,
2632e72448bSAnna Schumaker 				struct nfs_lock_context *dst_lock,
2649d8cacbfSTrond Myklebust 				struct nfs42_copy_args *args,
2651d38f3f0SOlga Kornievskaia 				struct nfs42_copy_res *res,
2661d38f3f0SOlga Kornievskaia 				struct nl4_server *nss,
2670e65a32cSOlga Kornievskaia 				nfs4_stateid *cnr_stateid,
2680e65a32cSOlga Kornievskaia 				bool *restart)
2692e72448bSAnna Schumaker {
2702e72448bSAnna Schumaker 	struct rpc_message msg = {
2712e72448bSAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY],
2729d8cacbfSTrond Myklebust 		.rpc_argp = args,
2739d8cacbfSTrond Myklebust 		.rpc_resp = res,
2742e72448bSAnna Schumaker 	};
2752e72448bSAnna Schumaker 	struct inode *dst_inode = file_inode(dst);
2760e65a32cSOlga Kornievskaia 	struct inode *src_inode = file_inode(src);
2770e65a32cSOlga Kornievskaia 	struct nfs_server *dst_server = NFS_SERVER(dst_inode);
2780e65a32cSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(src_inode);
2799d8cacbfSTrond Myklebust 	loff_t pos_src = args->src_pos;
2809d8cacbfSTrond Myklebust 	loff_t pos_dst = args->dst_pos;
2819d8cacbfSTrond Myklebust 	size_t count = args->count;
2821ee48bddSOlga Kornievskaia 	ssize_t status;
2832e72448bSAnna Schumaker 
2841d38f3f0SOlga Kornievskaia 	if (nss) {
2851d38f3f0SOlga Kornievskaia 		args->cp_src = nss;
2861d38f3f0SOlga Kornievskaia 		nfs4_stateid_copy(&args->src_stateid, cnr_stateid);
2871d38f3f0SOlga Kornievskaia 	} else {
2881d38f3f0SOlga Kornievskaia 		status = nfs4_set_rw_stateid(&args->src_stateid,
2891d38f3f0SOlga Kornievskaia 				src_lock->open_context, src_lock, FMODE_READ);
2902e72448bSAnna Schumaker 		if (status)
2912e72448bSAnna Schumaker 			return status;
2921d38f3f0SOlga Kornievskaia 	}
293837bb1d7STrond Myklebust 	status = nfs_filemap_write_and_wait_range(file_inode(src)->i_mapping,
294837bb1d7STrond Myklebust 			pos_src, pos_src + (loff_t)count - 1);
295837bb1d7STrond Myklebust 	if (status)
296837bb1d7STrond Myklebust 		return status;
297837bb1d7STrond Myklebust 
2989d8cacbfSTrond Myklebust 	status = nfs4_set_rw_stateid(&args->dst_stateid, dst_lock->open_context,
2992e72448bSAnna Schumaker 				     dst_lock, FMODE_WRITE);
3002e72448bSAnna Schumaker 	if (status)
3012e72448bSAnna Schumaker 		return status;
3022e72448bSAnna Schumaker 
303837bb1d7STrond Myklebust 	status = nfs_sync_inode(dst_inode);
304837bb1d7STrond Myklebust 	if (status)
305837bb1d7STrond Myklebust 		return status;
306837bb1d7STrond Myklebust 
30762164f31SOlga Kornievskaia 	res->commit_res.verf = NULL;
30862164f31SOlga Kornievskaia 	if (args->sync) {
30962164f31SOlga Kornievskaia 		res->commit_res.verf =
31062164f31SOlga Kornievskaia 			kzalloc(sizeof(struct nfs_writeverf), GFP_NOFS);
311e0926934SOlga Kornievskaia 		if (!res->commit_res.verf)
312e0926934SOlga Kornievskaia 			return -ENOMEM;
31362164f31SOlga Kornievskaia 	}
3140e65a32cSOlga Kornievskaia 	set_bit(NFS_CLNT_SRC_SSC_COPY_STATE,
3150e65a32cSOlga Kornievskaia 		&src_lock->open_context->state->flags);
316e4648aa4SOlga Kornievskaia 	set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
317e4648aa4SOlga Kornievskaia 		&dst_lock->open_context->state->flags);
318e4648aa4SOlga Kornievskaia 
3190e65a32cSOlga Kornievskaia 	status = nfs4_call_sync(dst_server->client, dst_server, &msg,
3209d8cacbfSTrond Myklebust 				&args->seq_args, &res->seq_res, 0);
3212e72448bSAnna Schumaker 	if (status == -ENOTSUPP)
3220e65a32cSOlga Kornievskaia 		dst_server->caps &= ~NFS_CAP_COPY;
3232e72448bSAnna Schumaker 	if (status)
324e0926934SOlga Kornievskaia 		goto out;
3252e72448bSAnna Schumaker 
32662164f31SOlga Kornievskaia 	if (args->sync &&
32762164f31SOlga Kornievskaia 		nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
328e0926934SOlga Kornievskaia 				    &res->commit_res.verf->verifier)) {
329e0926934SOlga Kornievskaia 		status = -EAGAIN;
330e0926934SOlga Kornievskaia 		goto out;
3312e72448bSAnna Schumaker 	}
3322e72448bSAnna Schumaker 
33362164f31SOlga Kornievskaia 	if (!res->synchronous) {
3340e65a32cSOlga Kornievskaia 		status = handle_async_copy(res, dst_server, src_server, src,
3350e65a32cSOlga Kornievskaia 				dst, &args->src_stateid, restart);
33662164f31SOlga Kornievskaia 		if (status)
33762164f31SOlga Kornievskaia 			return status;
33862164f31SOlga Kornievskaia 	}
33962164f31SOlga Kornievskaia 
3406b8d84e2SOlga Kornievskaia 	if ((!res->synchronous || !args->sync) &&
3416b8d84e2SOlga Kornievskaia 			res->write_res.verifier.committed != NFS_FILE_SYNC) {
3426b8d84e2SOlga Kornievskaia 		status = process_copy_commit(dst, pos_dst, res);
3436b8d84e2SOlga Kornievskaia 		if (status)
3446b8d84e2SOlga Kornievskaia 			return status;
3456b8d84e2SOlga Kornievskaia 	}
3466b8d84e2SOlga Kornievskaia 
3472e72448bSAnna Schumaker 	truncate_pagecache_range(dst_inode, pos_dst,
3489d8cacbfSTrond Myklebust 				 pos_dst + res->write_res.count);
3492e72448bSAnna Schumaker 
350e0926934SOlga Kornievskaia 	status = res->write_res.count;
351e0926934SOlga Kornievskaia out:
35262164f31SOlga Kornievskaia 	if (args->sync)
353e0926934SOlga Kornievskaia 		kfree(res->commit_res.verf);
354e0926934SOlga Kornievskaia 	return status;
3552e72448bSAnna Schumaker }
3562e72448bSAnna Schumaker 
3572e72448bSAnna Schumaker ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
3581d38f3f0SOlga Kornievskaia 			struct file *dst, loff_t pos_dst, size_t count,
3591d38f3f0SOlga Kornievskaia 			struct nl4_server *nss,
36012751010SOlga Kornievskaia 			nfs4_stateid *cnr_stateid, bool sync)
3612e72448bSAnna Schumaker {
3622e72448bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(dst));
3632e72448bSAnna Schumaker 	struct nfs_lock_context *src_lock;
3642e72448bSAnna Schumaker 	struct nfs_lock_context *dst_lock;
3659d8cacbfSTrond Myklebust 	struct nfs42_copy_args args = {
3669d8cacbfSTrond Myklebust 		.src_fh		= NFS_FH(file_inode(src)),
3679d8cacbfSTrond Myklebust 		.src_pos	= pos_src,
3689d8cacbfSTrond Myklebust 		.dst_fh		= NFS_FH(file_inode(dst)),
3699d8cacbfSTrond Myklebust 		.dst_pos	= pos_dst,
3709d8cacbfSTrond Myklebust 		.count		= count,
37112751010SOlga Kornievskaia 		.sync		= sync,
3729d8cacbfSTrond Myklebust 	};
3739d8cacbfSTrond Myklebust 	struct nfs42_copy_res res;
3749d8cacbfSTrond Myklebust 	struct nfs4_exception src_exception = {
3759d8cacbfSTrond Myklebust 		.inode		= file_inode(src),
3769d8cacbfSTrond Myklebust 		.stateid	= &args.src_stateid,
3779d8cacbfSTrond Myklebust 	};
3789d8cacbfSTrond Myklebust 	struct nfs4_exception dst_exception = {
3799d8cacbfSTrond Myklebust 		.inode		= file_inode(dst),
3809d8cacbfSTrond Myklebust 		.stateid	= &args.dst_stateid,
3819d8cacbfSTrond Myklebust 	};
3822e72448bSAnna Schumaker 	ssize_t err, err2;
3830e65a32cSOlga Kornievskaia 	bool restart = false;
3842e72448bSAnna Schumaker 
3852e72448bSAnna Schumaker 	src_lock = nfs_get_lock_context(nfs_file_open_context(src));
3862e72448bSAnna Schumaker 	if (IS_ERR(src_lock))
3872e72448bSAnna Schumaker 		return PTR_ERR(src_lock);
3882e72448bSAnna Schumaker 
3892e72448bSAnna Schumaker 	src_exception.state = src_lock->open_context->state;
3902e72448bSAnna Schumaker 
3912e72448bSAnna Schumaker 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst));
3922e72448bSAnna Schumaker 	if (IS_ERR(dst_lock)) {
3932e72448bSAnna Schumaker 		err = PTR_ERR(dst_lock);
3942e72448bSAnna Schumaker 		goto out_put_src_lock;
3952e72448bSAnna Schumaker 	}
3962e72448bSAnna Schumaker 
3972e72448bSAnna Schumaker 	dst_exception.state = dst_lock->open_context->state;
3982e72448bSAnna Schumaker 
3992e72448bSAnna Schumaker 	do {
400ea8ea737SLinus Torvalds 		inode_lock(file_inode(dst));
4019d8cacbfSTrond Myklebust 		err = _nfs42_proc_copy(src, src_lock,
4029d8cacbfSTrond Myklebust 				dst, dst_lock,
4031d38f3f0SOlga Kornievskaia 				&args, &res,
4040e65a32cSOlga Kornievskaia 				nss, cnr_stateid, &restart);
405ea8ea737SLinus Torvalds 		inode_unlock(file_inode(dst));
4062e72448bSAnna Schumaker 
4079d8cacbfSTrond Myklebust 		if (err >= 0)
4089d8cacbfSTrond Myklebust 			break;
40912406025SOlga Kornievskaia 		if (err == -ENOTSUPP &&
41012406025SOlga Kornievskaia 				nfs42_files_from_same_server(src, dst)) {
4112e72448bSAnna Schumaker 			err = -EOPNOTSUPP;
4122e72448bSAnna Schumaker 			break;
413539f57b3SOlga Kornievskaia 		} else if (err == -EAGAIN) {
4140e65a32cSOlga Kornievskaia 			if (!restart) {
415539f57b3SOlga Kornievskaia 				dst_exception.retry = 1;
416539f57b3SOlga Kornievskaia 				continue;
4170e65a32cSOlga Kornievskaia 			}
4180e65a32cSOlga Kornievskaia 			break;
419539f57b3SOlga Kornievskaia 		} else if (err == -NFS4ERR_OFFLOAD_NO_REQS && !args.sync) {
420539f57b3SOlga Kornievskaia 			args.sync = true;
421e0926934SOlga Kornievskaia 			dst_exception.retry = 1;
422e0926934SOlga Kornievskaia 			continue;
4236b61c969SOlga Kornievskaia 		} else if ((err == -ESTALE ||
42412406025SOlga Kornievskaia 				err == -NFS4ERR_OFFLOAD_DENIED ||
42512406025SOlga Kornievskaia 				err == -ENOTSUPP) &&
4267e350197SOlga Kornievskaia 				!nfs42_files_from_same_server(src, dst)) {
4277e350197SOlga Kornievskaia 			nfs42_do_offload_cancel_async(src, &args.src_stateid);
4287e350197SOlga Kornievskaia 			err = -EOPNOTSUPP;
4297e350197SOlga Kornievskaia 			break;
4302e72448bSAnna Schumaker 		}
4312e72448bSAnna Schumaker 
4322e72448bSAnna Schumaker 		err2 = nfs4_handle_exception(server, err, &src_exception);
4332e72448bSAnna Schumaker 		err  = nfs4_handle_exception(server, err, &dst_exception);
4342e72448bSAnna Schumaker 		if (!err)
4352e72448bSAnna Schumaker 			err = err2;
4362e72448bSAnna Schumaker 	} while (src_exception.retry || dst_exception.retry);
4372e72448bSAnna Schumaker 
4382e72448bSAnna Schumaker 	nfs_put_lock_context(dst_lock);
4392e72448bSAnna Schumaker out_put_src_lock:
4402e72448bSAnna Schumaker 	nfs_put_lock_context(src_lock);
4412e72448bSAnna Schumaker 	return err;
4422e72448bSAnna Schumaker }
4432e72448bSAnna Schumaker 
444c975c209SOlga Kornievskaia struct nfs42_offloadcancel_data {
445c975c209SOlga Kornievskaia 	struct nfs_server *seq_server;
446c975c209SOlga Kornievskaia 	struct nfs42_offload_status_args args;
447c975c209SOlga Kornievskaia 	struct nfs42_offload_status_res res;
448c975c209SOlga Kornievskaia };
449c975c209SOlga Kornievskaia 
450c975c209SOlga Kornievskaia static void nfs42_offload_cancel_prepare(struct rpc_task *task, void *calldata)
451c975c209SOlga Kornievskaia {
452c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
453c975c209SOlga Kornievskaia 
454c975c209SOlga Kornievskaia 	nfs4_setup_sequence(data->seq_server->nfs_client,
455c975c209SOlga Kornievskaia 				&data->args.osa_seq_args,
456c975c209SOlga Kornievskaia 				&data->res.osr_seq_res, task);
457c975c209SOlga Kornievskaia }
458c975c209SOlga Kornievskaia 
459c975c209SOlga Kornievskaia static void nfs42_offload_cancel_done(struct rpc_task *task, void *calldata)
460c975c209SOlga Kornievskaia {
461c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
462c975c209SOlga Kornievskaia 
463c975c209SOlga Kornievskaia 	nfs41_sequence_done(task, &data->res.osr_seq_res);
464c975c209SOlga Kornievskaia 	if (task->tk_status &&
465c975c209SOlga Kornievskaia 		nfs4_async_handle_error(task, data->seq_server, NULL,
466c975c209SOlga Kornievskaia 			NULL) == -EAGAIN)
467c975c209SOlga Kornievskaia 		rpc_restart_call_prepare(task);
468c975c209SOlga Kornievskaia }
469c975c209SOlga Kornievskaia 
470c975c209SOlga Kornievskaia static void nfs42_free_offloadcancel_data(void *data)
471c975c209SOlga Kornievskaia {
472c975c209SOlga Kornievskaia 	kfree(data);
473c975c209SOlga Kornievskaia }
474c975c209SOlga Kornievskaia 
475c975c209SOlga Kornievskaia static const struct rpc_call_ops nfs42_offload_cancel_ops = {
476c975c209SOlga Kornievskaia 	.rpc_call_prepare = nfs42_offload_cancel_prepare,
477c975c209SOlga Kornievskaia 	.rpc_call_done = nfs42_offload_cancel_done,
478c975c209SOlga Kornievskaia 	.rpc_release = nfs42_free_offloadcancel_data,
479c975c209SOlga Kornievskaia };
480c975c209SOlga Kornievskaia 
481c975c209SOlga Kornievskaia static int nfs42_do_offload_cancel_async(struct file *dst,
482c975c209SOlga Kornievskaia 					 nfs4_stateid *stateid)
483c975c209SOlga Kornievskaia {
484c975c209SOlga Kornievskaia 	struct nfs_server *dst_server = NFS_SERVER(file_inode(dst));
485c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = NULL;
486c975c209SOlga Kornievskaia 	struct nfs_open_context *ctx = nfs_file_open_context(dst);
487c975c209SOlga Kornievskaia 	struct rpc_task *task;
488c975c209SOlga Kornievskaia 	struct rpc_message msg = {
489c975c209SOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OFFLOAD_CANCEL],
490c975c209SOlga Kornievskaia 		.rpc_cred = ctx->cred,
491c975c209SOlga Kornievskaia 	};
492c975c209SOlga Kornievskaia 	struct rpc_task_setup task_setup_data = {
493c975c209SOlga Kornievskaia 		.rpc_client = dst_server->client,
494c975c209SOlga Kornievskaia 		.rpc_message = &msg,
495c975c209SOlga Kornievskaia 		.callback_ops = &nfs42_offload_cancel_ops,
496c975c209SOlga Kornievskaia 		.workqueue = nfsiod_workqueue,
497c975c209SOlga Kornievskaia 		.flags = RPC_TASK_ASYNC,
498c975c209SOlga Kornievskaia 	};
499c975c209SOlga Kornievskaia 	int status;
500c975c209SOlga Kornievskaia 
501c975c209SOlga Kornievskaia 	if (!(dst_server->caps & NFS_CAP_OFFLOAD_CANCEL))
502c975c209SOlga Kornievskaia 		return -EOPNOTSUPP;
503c975c209SOlga Kornievskaia 
504c975c209SOlga Kornievskaia 	data = kzalloc(sizeof(struct nfs42_offloadcancel_data), GFP_NOFS);
505c975c209SOlga Kornievskaia 	if (data == NULL)
506c975c209SOlga Kornievskaia 		return -ENOMEM;
507c975c209SOlga Kornievskaia 
508c975c209SOlga Kornievskaia 	data->seq_server = dst_server;
509c975c209SOlga Kornievskaia 	data->args.osa_src_fh = NFS_FH(file_inode(dst));
510c975c209SOlga Kornievskaia 	memcpy(&data->args.osa_stateid, stateid,
511c975c209SOlga Kornievskaia 		sizeof(data->args.osa_stateid));
512c975c209SOlga Kornievskaia 	msg.rpc_argp = &data->args;
513c975c209SOlga Kornievskaia 	msg.rpc_resp = &data->res;
514c975c209SOlga Kornievskaia 	task_setup_data.callback_data = data;
515c975c209SOlga Kornievskaia 	nfs4_init_sequence(&data->args.osa_seq_args, &data->res.osr_seq_res,
516c975c209SOlga Kornievskaia 			   1, 0);
517c975c209SOlga Kornievskaia 	task = rpc_run_task(&task_setup_data);
518c975c209SOlga Kornievskaia 	if (IS_ERR(task))
519c975c209SOlga Kornievskaia 		return PTR_ERR(task);
520c975c209SOlga Kornievskaia 	status = rpc_wait_for_completion_task(task);
521c975c209SOlga Kornievskaia 	if (status == -ENOTSUPP)
522c975c209SOlga Kornievskaia 		dst_server->caps &= ~NFS_CAP_OFFLOAD_CANCEL;
523c975c209SOlga Kornievskaia 	rpc_put_task(task);
524c975c209SOlga Kornievskaia 	return status;
525c975c209SOlga Kornievskaia }
526c975c209SOlga Kornievskaia 
5270491567bSOlga Kornievskaia int _nfs42_proc_copy_notify(struct file *src, struct file *dst,
5280491567bSOlga Kornievskaia 			    struct nfs42_copy_notify_args *args,
5290491567bSOlga Kornievskaia 			    struct nfs42_copy_notify_res *res)
5300491567bSOlga Kornievskaia {
5310491567bSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
5320491567bSOlga Kornievskaia 	struct rpc_message msg = {
5330491567bSOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY_NOTIFY],
5340491567bSOlga Kornievskaia 		.rpc_argp = args,
5350491567bSOlga Kornievskaia 		.rpc_resp = res,
5360491567bSOlga Kornievskaia 	};
5370491567bSOlga Kornievskaia 	int status;
5380491567bSOlga Kornievskaia 	struct nfs_open_context *ctx;
5390491567bSOlga Kornievskaia 	struct nfs_lock_context *l_ctx;
5400491567bSOlga Kornievskaia 
5410491567bSOlga Kornievskaia 	ctx = get_nfs_open_context(nfs_file_open_context(src));
5420491567bSOlga Kornievskaia 	l_ctx = nfs_get_lock_context(ctx);
5430491567bSOlga Kornievskaia 	if (IS_ERR(l_ctx))
5440491567bSOlga Kornievskaia 		return PTR_ERR(l_ctx);
5450491567bSOlga Kornievskaia 
5460491567bSOlga Kornievskaia 	status = nfs4_set_rw_stateid(&args->cna_src_stateid, ctx, l_ctx,
5470491567bSOlga Kornievskaia 				     FMODE_READ);
5480491567bSOlga Kornievskaia 	nfs_put_lock_context(l_ctx);
5490491567bSOlga Kornievskaia 	if (status)
5500491567bSOlga Kornievskaia 		return status;
5510491567bSOlga Kornievskaia 
5520491567bSOlga Kornievskaia 	status = nfs4_call_sync(src_server->client, src_server, &msg,
5530491567bSOlga Kornievskaia 				&args->cna_seq_args, &res->cnr_seq_res, 0);
5540491567bSOlga Kornievskaia 	if (status == -ENOTSUPP)
5550491567bSOlga Kornievskaia 		src_server->caps &= ~NFS_CAP_COPY_NOTIFY;
5560491567bSOlga Kornievskaia 
5570491567bSOlga Kornievskaia 	put_nfs_open_context(nfs_file_open_context(src));
5580491567bSOlga Kornievskaia 	return status;
5590491567bSOlga Kornievskaia }
5600491567bSOlga Kornievskaia 
5610491567bSOlga Kornievskaia int nfs42_proc_copy_notify(struct file *src, struct file *dst,
5620491567bSOlga Kornievskaia 				struct nfs42_copy_notify_res *res)
5630491567bSOlga Kornievskaia {
5640491567bSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
5650491567bSOlga Kornievskaia 	struct nfs42_copy_notify_args *args;
5660491567bSOlga Kornievskaia 	struct nfs4_exception exception = {
5670491567bSOlga Kornievskaia 		.inode = file_inode(src),
5680491567bSOlga Kornievskaia 	};
5690491567bSOlga Kornievskaia 	int status;
5700491567bSOlga Kornievskaia 
5710491567bSOlga Kornievskaia 	if (!(src_server->caps & NFS_CAP_COPY_NOTIFY))
5720491567bSOlga Kornievskaia 		return -EOPNOTSUPP;
5730491567bSOlga Kornievskaia 
5740491567bSOlga Kornievskaia 	args = kzalloc(sizeof(struct nfs42_copy_notify_args), GFP_NOFS);
5750491567bSOlga Kornievskaia 	if (args == NULL)
5760491567bSOlga Kornievskaia 		return -ENOMEM;
5770491567bSOlga Kornievskaia 
5780491567bSOlga Kornievskaia 	args->cna_src_fh  = NFS_FH(file_inode(src)),
5790491567bSOlga Kornievskaia 	args->cna_dst.nl4_type = NL4_NETADDR;
5800491567bSOlga Kornievskaia 	nfs42_set_netaddr(dst, &args->cna_dst.u.nl4_addr);
5810491567bSOlga Kornievskaia 	exception.stateid = &args->cna_src_stateid;
5820491567bSOlga Kornievskaia 
5830491567bSOlga Kornievskaia 	do {
5840491567bSOlga Kornievskaia 		status = _nfs42_proc_copy_notify(src, dst, args, res);
5850491567bSOlga Kornievskaia 		if (status == -ENOTSUPP) {
5860491567bSOlga Kornievskaia 			status = -EOPNOTSUPP;
5870491567bSOlga Kornievskaia 			goto out;
5880491567bSOlga Kornievskaia 		}
5890491567bSOlga Kornievskaia 		status = nfs4_handle_exception(src_server, status, &exception);
5900491567bSOlga Kornievskaia 	} while (exception.retry);
5910491567bSOlga Kornievskaia 
5920491567bSOlga Kornievskaia out:
5930491567bSOlga Kornievskaia 	kfree(args);
5940491567bSOlga Kornievskaia 	return status;
5950491567bSOlga Kornievskaia }
5960491567bSOlga Kornievskaia 
5974bdf87ebSChristoph Hellwig static loff_t _nfs42_proc_llseek(struct file *filep,
5984bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, int whence)
5991c6dcbe5SAnna Schumaker {
6001c6dcbe5SAnna Schumaker 	struct inode *inode = file_inode(filep);
6011c6dcbe5SAnna Schumaker 	struct nfs42_seek_args args = {
6021c6dcbe5SAnna Schumaker 		.sa_fh		= NFS_FH(inode),
6031c6dcbe5SAnna Schumaker 		.sa_offset	= offset,
6041c6dcbe5SAnna Schumaker 		.sa_what	= (whence == SEEK_HOLE) ?
6051c6dcbe5SAnna Schumaker 					NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA,
6061c6dcbe5SAnna Schumaker 	};
6071c6dcbe5SAnna Schumaker 	struct nfs42_seek_res res;
6081c6dcbe5SAnna Schumaker 	struct rpc_message msg = {
6091c6dcbe5SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK],
6101c6dcbe5SAnna Schumaker 		.rpc_argp = &args,
6111c6dcbe5SAnna Schumaker 		.rpc_resp = &res,
6121c6dcbe5SAnna Schumaker 	};
6131c6dcbe5SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
6141c6dcbe5SAnna Schumaker 	int status;
6151c6dcbe5SAnna Schumaker 
616878ffa9fSAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_SEEK))
6171c6dcbe5SAnna Schumaker 		return -ENOTSUPP;
6181c6dcbe5SAnna Schumaker 
6194bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context,
6204bdf87ebSChristoph Hellwig 			lock, FMODE_READ);
6211c6dcbe5SAnna Schumaker 	if (status)
6221c6dcbe5SAnna Schumaker 		return status;
6231c6dcbe5SAnna Schumaker 
624e95fc4a0STrond Myklebust 	status = nfs_filemap_write_and_wait_range(inode->i_mapping,
625e95fc4a0STrond Myklebust 			offset, LLONG_MAX);
626e95fc4a0STrond Myklebust 	if (status)
627e95fc4a0STrond Myklebust 		return status;
628e95fc4a0STrond Myklebust 
6291c6dcbe5SAnna Schumaker 	status = nfs4_call_sync(server->client, server, &msg,
6301c6dcbe5SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
6311c6dcbe5SAnna Schumaker 	if (status == -ENOTSUPP)
6321c6dcbe5SAnna Schumaker 		server->caps &= ~NFS_CAP_SEEK;
6331c6dcbe5SAnna Schumaker 	if (status)
6341c6dcbe5SAnna Schumaker 		return status;
6351c6dcbe5SAnna Schumaker 
6361c6dcbe5SAnna Schumaker 	return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes);
6371c6dcbe5SAnna Schumaker }
638be3a5d23STrond Myklebust 
639bdcc2cd1SJ. Bruce Fields loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
640bdcc2cd1SJ. Bruce Fields {
641bdcc2cd1SJ. Bruce Fields 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
642bdcc2cd1SJ. Bruce Fields 	struct nfs4_exception exception = { };
6434bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
644306a5549SJ. Bruce Fields 	loff_t err;
645bdcc2cd1SJ. Bruce Fields 
6464bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
6474bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
6484bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
6494bdf87ebSChristoph Hellwig 
6504bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
6514bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
6524bdf87ebSChristoph Hellwig 
653bdcc2cd1SJ. Bruce Fields 	do {
6544bdf87ebSChristoph Hellwig 		err = _nfs42_proc_llseek(filep, lock, offset, whence);
655306a5549SJ. Bruce Fields 		if (err >= 0)
656306a5549SJ. Bruce Fields 			break;
6574bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
6584bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
6594bdf87ebSChristoph Hellwig 			break;
6604bdf87ebSChristoph Hellwig 		}
661bdcc2cd1SJ. Bruce Fields 		err = nfs4_handle_exception(server, err, &exception);
662bdcc2cd1SJ. Bruce Fields 	} while (exception.retry);
663bdcc2cd1SJ. Bruce Fields 
6644bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
665bdcc2cd1SJ. Bruce Fields 	return err;
666bdcc2cd1SJ. Bruce Fields }
667bdcc2cd1SJ. Bruce Fields 
668bdcc2cd1SJ. Bruce Fields 
6691b4a4bd8SPeng Tao static void
6701b4a4bd8SPeng Tao nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata)
6711b4a4bd8SPeng Tao {
6721b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
6739a0fe867STrond Myklebust 	struct inode *inode = data->inode;
6749a0fe867STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
6759a0fe867STrond Myklebust 	struct pnfs_layout_hdr *lo;
6761b4a4bd8SPeng Tao 
6779a0fe867STrond Myklebust 	spin_lock(&inode->i_lock);
6789a0fe867STrond Myklebust 	lo = NFS_I(inode)->layout;
6799a0fe867STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
6809a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
6819a0fe867STrond Myklebust 		rpc_exit(task, 0);
6829a0fe867STrond Myklebust 		return;
6839a0fe867STrond Myklebust 	}
6849a0fe867STrond Myklebust 	nfs4_stateid_copy(&data->args.stateid, &lo->plh_stateid);
6859a0fe867STrond Myklebust 	spin_unlock(&inode->i_lock);
6866de7e12fSAnna Schumaker 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
6876de7e12fSAnna Schumaker 			    &data->res.seq_res, task);
6881b4a4bd8SPeng Tao }
6891b4a4bd8SPeng Tao 
6901b4a4bd8SPeng Tao static void
6911b4a4bd8SPeng Tao nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
6921b4a4bd8SPeng Tao {
6931b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
69468d264cfSPeng Tao 	struct inode *inode = data->inode;
69568d264cfSPeng Tao 	struct pnfs_layout_hdr *lo;
6961b4a4bd8SPeng Tao 
6971b4a4bd8SPeng Tao 	if (!nfs4_sequence_done(task, &data->res.seq_res))
6981b4a4bd8SPeng Tao 		return;
6991b4a4bd8SPeng Tao 
7006c5a0d89STrond Myklebust 	switch (task->tk_status) {
7016c5a0d89STrond Myklebust 	case 0:
7026c5a0d89STrond Myklebust 		break;
703cf61eb26STrond Myklebust 	case -NFS4ERR_BADHANDLE:
704cf61eb26STrond Myklebust 	case -ESTALE:
705cf61eb26STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
706cf61eb26STrond Myklebust 		break;
70768d264cfSPeng Tao 	case -NFS4ERR_EXPIRED:
708206b3bb5STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
709206b3bb5STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
71068d264cfSPeng Tao 	case -NFS4ERR_STALE_STATEID:
71168d264cfSPeng Tao 	case -NFS4ERR_BAD_STATEID:
71268d264cfSPeng Tao 		spin_lock(&inode->i_lock);
71368d264cfSPeng Tao 		lo = NFS_I(inode)->layout;
7149a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
7159a0fe867STrond Myklebust 		    nfs4_stateid_match(&data->args.stateid,
71668d264cfSPeng Tao 					     &lo->plh_stateid)) {
71768d264cfSPeng Tao 			LIST_HEAD(head);
71868d264cfSPeng Tao 
71968d264cfSPeng Tao 			/*
72068d264cfSPeng Tao 			 * Mark the bad layout state as invalid, then retry
72168d264cfSPeng Tao 			 * with the current stateid.
72268d264cfSPeng Tao 			 */
7235f46be04STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
72468d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
72568d264cfSPeng Tao 			pnfs_free_lseg_list(&head);
7261f18b82cSTrond Myklebust 			nfs_commit_inode(inode, 0);
72768d264cfSPeng Tao 		} else
72868d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
72968d264cfSPeng Tao 		break;
7309a0fe867STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
7319a0fe867STrond Myklebust 		spin_lock(&inode->i_lock);
7329a0fe867STrond Myklebust 		lo = NFS_I(inode)->layout;
7339a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
7349a0fe867STrond Myklebust 		    nfs4_stateid_match_other(&data->args.stateid,
7359a0fe867STrond Myklebust 					&lo->plh_stateid)) {
7369a0fe867STrond Myklebust 			/* Do we need to delay before resending? */
7379a0fe867STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
7389a0fe867STrond Myklebust 						&data->args.stateid))
7399a0fe867STrond Myklebust 				rpc_delay(task, HZ);
7409a0fe867STrond Myklebust 			rpc_restart_call_prepare(task);
7419a0fe867STrond Myklebust 		}
7429a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
7439a0fe867STrond Myklebust 		break;
7446c5a0d89STrond Myklebust 	case -ENOTSUPP:
7456c5a0d89STrond Myklebust 	case -EOPNOTSUPP:
74668d264cfSPeng Tao 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
7471b4a4bd8SPeng Tao 	}
7486c5a0d89STrond Myklebust }
7491b4a4bd8SPeng Tao 
7501b4a4bd8SPeng Tao static void
7511b4a4bd8SPeng Tao nfs42_layoutstat_release(void *calldata)
7521b4a4bd8SPeng Tao {
7531b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
754422c93c8STrond Myklebust 	struct nfs42_layoutstat_devinfo *devinfo = data->args.devinfo;
755422c93c8STrond Myklebust 	int i;
7568733408dSPeng Tao 
757422c93c8STrond Myklebust 	for (i = 0; i < data->args.num_dev; i++) {
758422c93c8STrond Myklebust 		if (devinfo[i].ld_private.ops && devinfo[i].ld_private.ops->free)
759422c93c8STrond Myklebust 			devinfo[i].ld_private.ops->free(&devinfo[i].ld_private);
760422c93c8STrond Myklebust 	}
7611b4a4bd8SPeng Tao 
7621b4a4bd8SPeng Tao 	pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout);
7631bfe3b25SPeng Tao 	smp_mb__before_atomic();
7641bfe3b25SPeng Tao 	clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags);
7651bfe3b25SPeng Tao 	smp_mb__after_atomic();
7661b4a4bd8SPeng Tao 	nfs_iput_and_deactive(data->inode);
7671b4a4bd8SPeng Tao 	kfree(data->args.devinfo);
7681b4a4bd8SPeng Tao 	kfree(data);
7691b4a4bd8SPeng Tao }
7701b4a4bd8SPeng Tao 
771be3a5d23STrond Myklebust static const struct rpc_call_ops nfs42_layoutstat_ops = {
7721b4a4bd8SPeng Tao 	.rpc_call_prepare = nfs42_layoutstat_prepare,
7731b4a4bd8SPeng Tao 	.rpc_call_done = nfs42_layoutstat_done,
7741b4a4bd8SPeng Tao 	.rpc_release = nfs42_layoutstat_release,
775be3a5d23STrond Myklebust };
776be3a5d23STrond Myklebust 
777be3a5d23STrond Myklebust int nfs42_proc_layoutstats_generic(struct nfs_server *server,
778be3a5d23STrond Myklebust 				   struct nfs42_layoutstat_data *data)
779be3a5d23STrond Myklebust {
780be3a5d23STrond Myklebust 	struct rpc_message msg = {
781be3a5d23STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS],
782be3a5d23STrond Myklebust 		.rpc_argp = &data->args,
783be3a5d23STrond Myklebust 		.rpc_resp = &data->res,
784be3a5d23STrond Myklebust 	};
785be3a5d23STrond Myklebust 	struct rpc_task_setup task_setup = {
786be3a5d23STrond Myklebust 		.rpc_client = server->client,
787be3a5d23STrond Myklebust 		.rpc_message = &msg,
788be3a5d23STrond Myklebust 		.callback_ops = &nfs42_layoutstat_ops,
789be3a5d23STrond Myklebust 		.callback_data = data,
790be3a5d23STrond Myklebust 		.flags = RPC_TASK_ASYNC,
791be3a5d23STrond Myklebust 	};
792be3a5d23STrond Myklebust 	struct rpc_task *task;
793be3a5d23STrond Myklebust 
7941b4a4bd8SPeng Tao 	data->inode = nfs_igrab_and_active(data->args.inode);
7951b4a4bd8SPeng Tao 	if (!data->inode) {
7961b4a4bd8SPeng Tao 		nfs42_layoutstat_release(data);
7971b4a4bd8SPeng Tao 		return -EAGAIN;
7981b4a4bd8SPeng Tao 	}
799fba83f34SAnna Schumaker 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
800be3a5d23STrond Myklebust 	task = rpc_run_task(&task_setup);
801be3a5d23STrond Myklebust 	if (IS_ERR(task))
802be3a5d23STrond Myklebust 		return PTR_ERR(task);
8033f807e5aSJeff Layton 	rpc_put_task(task);
804be3a5d23STrond Myklebust 	return 0;
805be3a5d23STrond Myklebust }
806e5341f3aSPeng Tao 
8073eb86093STrond Myklebust static struct nfs42_layouterror_data *
8083eb86093STrond Myklebust nfs42_alloc_layouterror_data(struct pnfs_layout_segment *lseg, gfp_t gfp_flags)
8093eb86093STrond Myklebust {
8103eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
8113eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
8123eb86093STrond Myklebust 
8133eb86093STrond Myklebust 	data = kzalloc(sizeof(*data), gfp_flags);
8143eb86093STrond Myklebust 	if (data) {
8153eb86093STrond Myklebust 		data->args.inode = data->inode = nfs_igrab_and_active(inode);
8163eb86093STrond Myklebust 		if (data->inode) {
8173eb86093STrond Myklebust 			data->lseg = pnfs_get_lseg(lseg);
8183eb86093STrond Myklebust 			if (data->lseg)
8193eb86093STrond Myklebust 				return data;
8203eb86093STrond Myklebust 			nfs_iput_and_deactive(data->inode);
8213eb86093STrond Myklebust 		}
8223eb86093STrond Myklebust 		kfree(data);
8233eb86093STrond Myklebust 	}
8243eb86093STrond Myklebust 	return NULL;
8253eb86093STrond Myklebust }
8263eb86093STrond Myklebust 
8273eb86093STrond Myklebust static void
8283eb86093STrond Myklebust nfs42_free_layouterror_data(struct nfs42_layouterror_data *data)
8293eb86093STrond Myklebust {
8303eb86093STrond Myklebust 	pnfs_put_lseg(data->lseg);
8313eb86093STrond Myklebust 	nfs_iput_and_deactive(data->inode);
8323eb86093STrond Myklebust 	kfree(data);
8333eb86093STrond Myklebust }
8343eb86093STrond Myklebust 
8353eb86093STrond Myklebust static void
8363eb86093STrond Myklebust nfs42_layouterror_prepare(struct rpc_task *task, void *calldata)
8373eb86093STrond Myklebust {
8383eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
8393eb86093STrond Myklebust 	struct inode *inode = data->inode;
8403eb86093STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
8413eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
8423eb86093STrond Myklebust 	unsigned i;
8433eb86093STrond Myklebust 
8443eb86093STrond Myklebust 	spin_lock(&inode->i_lock);
8453eb86093STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
8463eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
8473eb86093STrond Myklebust 		rpc_exit(task, 0);
8483eb86093STrond Myklebust 		return;
8493eb86093STrond Myklebust 	}
8503eb86093STrond Myklebust 	for (i = 0; i < data->args.num_errors; i++)
8513eb86093STrond Myklebust 		nfs4_stateid_copy(&data->args.errors[i].stateid,
8523eb86093STrond Myklebust 				&lo->plh_stateid);
8533eb86093STrond Myklebust 	spin_unlock(&inode->i_lock);
8543eb86093STrond Myklebust 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
8553eb86093STrond Myklebust 			    &data->res.seq_res, task);
8563eb86093STrond Myklebust }
8573eb86093STrond Myklebust 
8583eb86093STrond Myklebust static void
8593eb86093STrond Myklebust nfs42_layouterror_done(struct rpc_task *task, void *calldata)
8603eb86093STrond Myklebust {
8613eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
8623eb86093STrond Myklebust 	struct inode *inode = data->inode;
8633eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
8643eb86093STrond Myklebust 
8653eb86093STrond Myklebust 	if (!nfs4_sequence_done(task, &data->res.seq_res))
8663eb86093STrond Myklebust 		return;
8673eb86093STrond Myklebust 
8683eb86093STrond Myklebust 	switch (task->tk_status) {
8693eb86093STrond Myklebust 	case 0:
8703eb86093STrond Myklebust 		break;
8713eb86093STrond Myklebust 	case -NFS4ERR_BADHANDLE:
8723eb86093STrond Myklebust 	case -ESTALE:
8733eb86093STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
8743eb86093STrond Myklebust 		break;
8753eb86093STrond Myklebust 	case -NFS4ERR_EXPIRED:
8763eb86093STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
8773eb86093STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
8783eb86093STrond Myklebust 	case -NFS4ERR_STALE_STATEID:
8793eb86093STrond Myklebust 	case -NFS4ERR_BAD_STATEID:
8803eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
8813eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
8823eb86093STrond Myklebust 		    nfs4_stateid_match(&data->args.errors[0].stateid,
8833eb86093STrond Myklebust 					     &lo->plh_stateid)) {
8843eb86093STrond Myklebust 			LIST_HEAD(head);
8853eb86093STrond Myklebust 
8863eb86093STrond Myklebust 			/*
8873eb86093STrond Myklebust 			 * Mark the bad layout state as invalid, then retry
8883eb86093STrond Myklebust 			 * with the current stateid.
8893eb86093STrond Myklebust 			 */
8903eb86093STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
8913eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
8923eb86093STrond Myklebust 			pnfs_free_lseg_list(&head);
8933eb86093STrond Myklebust 			nfs_commit_inode(inode, 0);
8943eb86093STrond Myklebust 		} else
8953eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
8963eb86093STrond Myklebust 		break;
8973eb86093STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
8983eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
8993eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
9003eb86093STrond Myklebust 		    nfs4_stateid_match_other(&data->args.errors[0].stateid,
9013eb86093STrond Myklebust 					&lo->plh_stateid)) {
9023eb86093STrond Myklebust 			/* Do we need to delay before resending? */
9033eb86093STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
9043eb86093STrond Myklebust 						&data->args.errors[0].stateid))
9053eb86093STrond Myklebust 				rpc_delay(task, HZ);
9063eb86093STrond Myklebust 			rpc_restart_call_prepare(task);
9073eb86093STrond Myklebust 		}
9083eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
9093eb86093STrond Myklebust 		break;
9103eb86093STrond Myklebust 	case -ENOTSUPP:
9113eb86093STrond Myklebust 	case -EOPNOTSUPP:
9123eb86093STrond Myklebust 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTERROR;
9133eb86093STrond Myklebust 	}
9143eb86093STrond Myklebust }
9153eb86093STrond Myklebust 
9163eb86093STrond Myklebust static void
9173eb86093STrond Myklebust nfs42_layouterror_release(void *calldata)
9183eb86093STrond Myklebust {
9193eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
9203eb86093STrond Myklebust 
9213eb86093STrond Myklebust 	nfs42_free_layouterror_data(data);
9223eb86093STrond Myklebust }
9233eb86093STrond Myklebust 
9243eb86093STrond Myklebust static const struct rpc_call_ops nfs42_layouterror_ops = {
9253eb86093STrond Myklebust 	.rpc_call_prepare = nfs42_layouterror_prepare,
9263eb86093STrond Myklebust 	.rpc_call_done = nfs42_layouterror_done,
9273eb86093STrond Myklebust 	.rpc_release = nfs42_layouterror_release,
9283eb86093STrond Myklebust };
9293eb86093STrond Myklebust 
9303eb86093STrond Myklebust int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
9313eb86093STrond Myklebust 		const struct nfs42_layout_error *errors, size_t n)
9323eb86093STrond Myklebust {
9333eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
9343eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
9353eb86093STrond Myklebust 	struct rpc_task *task;
9363eb86093STrond Myklebust 	struct rpc_message msg = {
9373eb86093STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTERROR],
9383eb86093STrond Myklebust 	};
9393eb86093STrond Myklebust 	struct rpc_task_setup task_setup = {
9403eb86093STrond Myklebust 		.rpc_message = &msg,
9413eb86093STrond Myklebust 		.callback_ops = &nfs42_layouterror_ops,
9423eb86093STrond Myklebust 		.flags = RPC_TASK_ASYNC,
9433eb86093STrond Myklebust 	};
9443eb86093STrond Myklebust 	unsigned int i;
9453eb86093STrond Myklebust 
9463eb86093STrond Myklebust 	if (!nfs_server_capable(inode, NFS_CAP_LAYOUTERROR))
9473eb86093STrond Myklebust 		return -EOPNOTSUPP;
9483eb86093STrond Myklebust 	if (n > NFS42_LAYOUTERROR_MAX)
9493eb86093STrond Myklebust 		return -EINVAL;
9503eb86093STrond Myklebust 	data = nfs42_alloc_layouterror_data(lseg, GFP_NOFS);
9513eb86093STrond Myklebust 	if (!data)
9523eb86093STrond Myklebust 		return -ENOMEM;
9533eb86093STrond Myklebust 	for (i = 0; i < n; i++) {
9543eb86093STrond Myklebust 		data->args.errors[i] = errors[i];
9553eb86093STrond Myklebust 		data->args.num_errors++;
9563eb86093STrond Myklebust 		data->res.num_errors++;
9573eb86093STrond Myklebust 	}
9583eb86093STrond Myklebust 	msg.rpc_argp = &data->args;
9593eb86093STrond Myklebust 	msg.rpc_resp = &data->res;
9603eb86093STrond Myklebust 	task_setup.callback_data = data;
9613eb86093STrond Myklebust 	task_setup.rpc_client = NFS_SERVER(inode)->client;
9623eb86093STrond Myklebust 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
9633eb86093STrond Myklebust 	task = rpc_run_task(&task_setup);
9643eb86093STrond Myklebust 	if (IS_ERR(task))
9653eb86093STrond Myklebust 		return PTR_ERR(task);
9663eb86093STrond Myklebust 	rpc_put_task(task);
9673eb86093STrond Myklebust 	return 0;
9683eb86093STrond Myklebust }
9693eb86093STrond Myklebust EXPORT_SYMBOL_GPL(nfs42_proc_layouterror);
9703eb86093STrond Myklebust 
971e5341f3aSPeng Tao static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
9724bdf87ebSChristoph Hellwig 		struct file *dst_f, struct nfs_lock_context *src_lock,
9734bdf87ebSChristoph Hellwig 		struct nfs_lock_context *dst_lock, loff_t src_offset,
974e5341f3aSPeng Tao 		loff_t dst_offset, loff_t count)
975e5341f3aSPeng Tao {
976e5341f3aSPeng Tao 	struct inode *src_inode = file_inode(src_f);
977e5341f3aSPeng Tao 	struct inode *dst_inode = file_inode(dst_f);
978e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(dst_inode);
979e5341f3aSPeng Tao 	struct nfs42_clone_args args = {
980e5341f3aSPeng Tao 		.src_fh = NFS_FH(src_inode),
981e5341f3aSPeng Tao 		.dst_fh = NFS_FH(dst_inode),
982e5341f3aSPeng Tao 		.src_offset = src_offset,
983e5341f3aSPeng Tao 		.dst_offset = dst_offset,
9849494b2ceSChristoph Hellwig 		.count = count,
985e5341f3aSPeng Tao 		.dst_bitmask = server->cache_consistency_bitmask,
986e5341f3aSPeng Tao 	};
987e5341f3aSPeng Tao 	struct nfs42_clone_res res = {
988e5341f3aSPeng Tao 		.server	= server,
989e5341f3aSPeng Tao 	};
990e5341f3aSPeng Tao 	int status;
991e5341f3aSPeng Tao 
992e5341f3aSPeng Tao 	msg->rpc_argp = &args;
993e5341f3aSPeng Tao 	msg->rpc_resp = &res;
994e5341f3aSPeng Tao 
9954bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context,
9964bdf87ebSChristoph Hellwig 			src_lock, FMODE_READ);
997e5341f3aSPeng Tao 	if (status)
998e5341f3aSPeng Tao 		return status;
999e5341f3aSPeng Tao 
10004bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
10014bdf87ebSChristoph Hellwig 			dst_lock, FMODE_WRITE);
1002e5341f3aSPeng Tao 	if (status)
1003e5341f3aSPeng Tao 		return status;
1004e5341f3aSPeng Tao 
1005e5341f3aSPeng Tao 	res.dst_fattr = nfs_alloc_fattr();
1006e5341f3aSPeng Tao 	if (!res.dst_fattr)
1007e5341f3aSPeng Tao 		return -ENOMEM;
1008e5341f3aSPeng Tao 
1009e5341f3aSPeng Tao 	status = nfs4_call_sync(server->client, server, msg,
1010e5341f3aSPeng Tao 				&args.seq_args, &res.seq_res, 0);
1011e5341f3aSPeng Tao 	if (status == 0)
1012e5341f3aSPeng Tao 		status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
1013e5341f3aSPeng Tao 
1014e5341f3aSPeng Tao 	kfree(res.dst_fattr);
1015e5341f3aSPeng Tao 	return status;
1016e5341f3aSPeng Tao }
1017e5341f3aSPeng Tao 
1018e5341f3aSPeng Tao int nfs42_proc_clone(struct file *src_f, struct file *dst_f,
1019e5341f3aSPeng Tao 		     loff_t src_offset, loff_t dst_offset, loff_t count)
1020e5341f3aSPeng Tao {
1021e5341f3aSPeng Tao 	struct rpc_message msg = {
1022e5341f3aSPeng Tao 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE],
1023e5341f3aSPeng Tao 	};
1024e5341f3aSPeng Tao 	struct inode *inode = file_inode(src_f);
1025e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(file_inode(src_f));
10264bdf87ebSChristoph Hellwig 	struct nfs_lock_context *src_lock;
10274bdf87ebSChristoph Hellwig 	struct nfs_lock_context *dst_lock;
10284bdf87ebSChristoph Hellwig 	struct nfs4_exception src_exception = { };
10294bdf87ebSChristoph Hellwig 	struct nfs4_exception dst_exception = { };
10304bdf87ebSChristoph Hellwig 	int err, err2;
1031e5341f3aSPeng Tao 
1032e5341f3aSPeng Tao 	if (!nfs_server_capable(inode, NFS_CAP_CLONE))
1033e5341f3aSPeng Tao 		return -EOPNOTSUPP;
1034e5341f3aSPeng Tao 
10354bdf87ebSChristoph Hellwig 	src_lock = nfs_get_lock_context(nfs_file_open_context(src_f));
10364bdf87ebSChristoph Hellwig 	if (IS_ERR(src_lock))
10374bdf87ebSChristoph Hellwig 		return PTR_ERR(src_lock);
10384bdf87ebSChristoph Hellwig 
10394bdf87ebSChristoph Hellwig 	src_exception.inode = file_inode(src_f);
10404bdf87ebSChristoph Hellwig 	src_exception.state = src_lock->open_context->state;
10414bdf87ebSChristoph Hellwig 
10424bdf87ebSChristoph Hellwig 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f));
10434bdf87ebSChristoph Hellwig 	if (IS_ERR(dst_lock)) {
10444bdf87ebSChristoph Hellwig 		err = PTR_ERR(dst_lock);
10454bdf87ebSChristoph Hellwig 		goto out_put_src_lock;
10464bdf87ebSChristoph Hellwig 	}
10474bdf87ebSChristoph Hellwig 
10484bdf87ebSChristoph Hellwig 	dst_exception.inode = file_inode(dst_f);
10494bdf87ebSChristoph Hellwig 	dst_exception.state = dst_lock->open_context->state;
10504bdf87ebSChristoph Hellwig 
1051e5341f3aSPeng Tao 	do {
10524bdf87ebSChristoph Hellwig 		err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock,
10534bdf87ebSChristoph Hellwig 					src_offset, dst_offset, count);
1054e5341f3aSPeng Tao 		if (err == -ENOTSUPP || err == -EOPNOTSUPP) {
1055e5341f3aSPeng Tao 			NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE;
10564bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
10574bdf87ebSChristoph Hellwig 			break;
1058e5341f3aSPeng Tao 		}
1059e5341f3aSPeng Tao 
10604bdf87ebSChristoph Hellwig 		err2 = nfs4_handle_exception(server, err, &src_exception);
10614bdf87ebSChristoph Hellwig 		err = nfs4_handle_exception(server, err, &dst_exception);
10624bdf87ebSChristoph Hellwig 		if (!err)
10634bdf87ebSChristoph Hellwig 			err = err2;
10644bdf87ebSChristoph Hellwig 	} while (src_exception.retry || dst_exception.retry);
10654bdf87ebSChristoph Hellwig 
10664bdf87ebSChristoph Hellwig 	nfs_put_lock_context(dst_lock);
10674bdf87ebSChristoph Hellwig out_put_src_lock:
10684bdf87ebSChristoph Hellwig 	nfs_put_lock_context(src_lock);
1069e5341f3aSPeng Tao 	return err;
1070e5341f3aSPeng Tao }
1071