xref: /openbmc/linux/fs/nfs/nfs42proc.c (revision d826e5b8)
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);
64d826e5b8SOlga Kornievskaia 	if (status) {
65d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
66d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
67f4ac1674SAnna Schumaker 		return status;
68d826e5b8SOlga Kornievskaia 	}
69f4ac1674SAnna Schumaker 
709a51940bSAnna Schumaker 	res.falloc_fattr = nfs_alloc_fattr();
719a51940bSAnna Schumaker 	if (!res.falloc_fattr)
729a51940bSAnna Schumaker 		return -ENOMEM;
739a51940bSAnna Schumaker 
749a51940bSAnna Schumaker 	status = nfs4_call_sync(server->client, server, msg,
75f4ac1674SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
769a51940bSAnna Schumaker 	if (status == 0)
779a51940bSAnna Schumaker 		status = nfs_post_op_update_inode(inode, res.falloc_fattr);
789a51940bSAnna Schumaker 
799a51940bSAnna Schumaker 	kfree(res.falloc_fattr);
809a51940bSAnna Schumaker 	return status;
81f4ac1674SAnna Schumaker }
82f4ac1674SAnna Schumaker 
83f4ac1674SAnna Schumaker static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
84f4ac1674SAnna Schumaker 				loff_t offset, loff_t len)
85f4ac1674SAnna Schumaker {
86f4ac1674SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
87f4ac1674SAnna Schumaker 	struct nfs4_exception exception = { };
884bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
89f4ac1674SAnna Schumaker 	int err;
90f4ac1674SAnna Schumaker 
914bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
924bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
934bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
944bdf87ebSChristoph Hellwig 
954bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
964bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
974bdf87ebSChristoph Hellwig 
98f4ac1674SAnna Schumaker 	do {
994bdf87ebSChristoph Hellwig 		err = _nfs42_proc_fallocate(msg, filep, lock, offset, len);
1004bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
1014bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
1024bdf87ebSChristoph Hellwig 			break;
1034bdf87ebSChristoph Hellwig 		}
104f4ac1674SAnna Schumaker 		err = nfs4_handle_exception(server, err, &exception);
105f4ac1674SAnna Schumaker 	} while (exception.retry);
106f4ac1674SAnna Schumaker 
1074bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
108f4ac1674SAnna Schumaker 	return err;
109f4ac1674SAnna Schumaker }
110f4ac1674SAnna Schumaker 
111f4ac1674SAnna Schumaker int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
112f4ac1674SAnna Schumaker {
113f4ac1674SAnna Schumaker 	struct rpc_message msg = {
114f4ac1674SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE],
115f4ac1674SAnna Schumaker 	};
116f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
117f4ac1674SAnna Schumaker 	int err;
118f4ac1674SAnna Schumaker 
119f4ac1674SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE))
120f4ac1674SAnna Schumaker 		return -EOPNOTSUPP;
121f4ac1674SAnna Schumaker 
1225955102cSAl Viro 	inode_lock(inode);
123f830f7ddSAnna Schumaker 
124f4ac1674SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
125f4ac1674SAnna Schumaker 	if (err == -EOPNOTSUPP)
126f4ac1674SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE;
127f830f7ddSAnna Schumaker 
1285955102cSAl Viro 	inode_unlock(inode);
129f4ac1674SAnna Schumaker 	return err;
130f4ac1674SAnna Schumaker }
131f4ac1674SAnna Schumaker 
132624bd5b7SAnna Schumaker int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
133624bd5b7SAnna Schumaker {
134624bd5b7SAnna Schumaker 	struct rpc_message msg = {
135624bd5b7SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE],
136624bd5b7SAnna Schumaker 	};
137624bd5b7SAnna Schumaker 	struct inode *inode = file_inode(filep);
138624bd5b7SAnna Schumaker 	int err;
139624bd5b7SAnna Schumaker 
140624bd5b7SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE))
141624bd5b7SAnna Schumaker 		return -EOPNOTSUPP;
142624bd5b7SAnna Schumaker 
1435955102cSAl Viro 	inode_lock(inode);
1441e564d3dSTrond Myklebust 	err = nfs_sync_inode(inode);
1451e564d3dSTrond Myklebust 	if (err)
1461e564d3dSTrond Myklebust 		goto out_unlock;
147f830f7ddSAnna Schumaker 
148624bd5b7SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
1499a51940bSAnna Schumaker 	if (err == 0)
1509a51940bSAnna Schumaker 		truncate_pagecache_range(inode, offset, (offset + len) -1);
151624bd5b7SAnna Schumaker 	if (err == -EOPNOTSUPP)
152624bd5b7SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE;
1531e564d3dSTrond Myklebust out_unlock:
1545955102cSAl Viro 	inode_unlock(inode);
155624bd5b7SAnna Schumaker 	return err;
156624bd5b7SAnna Schumaker }
157624bd5b7SAnna Schumaker 
15862164f31SOlga Kornievskaia static int handle_async_copy(struct nfs42_copy_res *res,
1590e65a32cSOlga Kornievskaia 			     struct nfs_server *dst_server,
1600e65a32cSOlga Kornievskaia 			     struct nfs_server *src_server,
16162164f31SOlga Kornievskaia 			     struct file *src,
16262164f31SOlga Kornievskaia 			     struct file *dst,
1630e65a32cSOlga Kornievskaia 			     nfs4_stateid *src_stateid,
1640e65a32cSOlga Kornievskaia 			     bool *restart)
16562164f31SOlga Kornievskaia {
16699f2c555SOlga Kornievskaia 	struct nfs4_copy_state *copy, *tmp_copy;
16762164f31SOlga Kornievskaia 	int status = NFS4_OK;
168bc0c9079SOlga Kornievskaia 	bool found_pending = false;
1690e65a32cSOlga Kornievskaia 	struct nfs_open_context *dst_ctx = nfs_file_open_context(dst);
1700e65a32cSOlga Kornievskaia 	struct nfs_open_context *src_ctx = nfs_file_open_context(src);
171bc0c9079SOlga Kornievskaia 
17299f2c555SOlga Kornievskaia 	copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
17399f2c555SOlga Kornievskaia 	if (!copy)
17499f2c555SOlga Kornievskaia 		return -ENOMEM;
17599f2c555SOlga Kornievskaia 
1760e65a32cSOlga Kornievskaia 	spin_lock(&dst_server->nfs_client->cl_lock);
1770e65a32cSOlga Kornievskaia 	list_for_each_entry(tmp_copy,
1780e65a32cSOlga Kornievskaia 				&dst_server->nfs_client->pending_cb_stateids,
179bc0c9079SOlga Kornievskaia 				copies) {
18099f2c555SOlga Kornievskaia 		if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
181bc0c9079SOlga Kornievskaia 				NFS4_STATEID_SIZE))
182bc0c9079SOlga Kornievskaia 			continue;
183bc0c9079SOlga Kornievskaia 		found_pending = true;
18499f2c555SOlga Kornievskaia 		list_del(&tmp_copy->copies);
185bc0c9079SOlga Kornievskaia 		break;
186bc0c9079SOlga Kornievskaia 	}
187bc0c9079SOlga Kornievskaia 	if (found_pending) {
1880e65a32cSOlga Kornievskaia 		spin_unlock(&dst_server->nfs_client->cl_lock);
18999f2c555SOlga Kornievskaia 		kfree(copy);
19099f2c555SOlga Kornievskaia 		copy = tmp_copy;
191bc0c9079SOlga Kornievskaia 		goto out;
192bc0c9079SOlga Kornievskaia 	}
19362164f31SOlga Kornievskaia 
19462164f31SOlga Kornievskaia 	memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
19562164f31SOlga Kornievskaia 	init_completion(&copy->completion);
1960e65a32cSOlga Kornievskaia 	copy->parent_dst_state = dst_ctx->state;
1970e65a32cSOlga Kornievskaia 	copy->parent_src_state = src_ctx->state;
19862164f31SOlga Kornievskaia 
1990e65a32cSOlga Kornievskaia 	list_add_tail(&copy->copies, &dst_server->ss_copies);
2000e65a32cSOlga Kornievskaia 	spin_unlock(&dst_server->nfs_client->cl_lock);
2010e65a32cSOlga Kornievskaia 
2020e65a32cSOlga Kornievskaia 	if (dst_server != src_server) {
2030e65a32cSOlga Kornievskaia 		spin_lock(&src_server->nfs_client->cl_lock);
2040e65a32cSOlga Kornievskaia 		list_add_tail(&copy->src_copies, &src_server->ss_copies);
2050e65a32cSOlga Kornievskaia 		spin_unlock(&src_server->nfs_client->cl_lock);
2060e65a32cSOlga Kornievskaia 	}
20762164f31SOlga Kornievskaia 
208c975c209SOlga Kornievskaia 	status = wait_for_completion_interruptible(&copy->completion);
2090e65a32cSOlga Kornievskaia 	spin_lock(&dst_server->nfs_client->cl_lock);
21062164f31SOlga Kornievskaia 	list_del_init(&copy->copies);
2110e65a32cSOlga Kornievskaia 	spin_unlock(&dst_server->nfs_client->cl_lock);
2120e65a32cSOlga Kornievskaia 	if (dst_server != src_server) {
2130e65a32cSOlga Kornievskaia 		spin_lock(&src_server->nfs_client->cl_lock);
2140e65a32cSOlga Kornievskaia 		list_del_init(&copy->src_copies);
2150e65a32cSOlga Kornievskaia 		spin_unlock(&src_server->nfs_client->cl_lock);
2160e65a32cSOlga Kornievskaia 	}
217c975c209SOlga Kornievskaia 	if (status == -ERESTARTSYS) {
218e4648aa4SOlga Kornievskaia 		goto out_cancel;
2190e65a32cSOlga Kornievskaia 	} else if (copy->flags || copy->error == NFS4ERR_PARTNER_NO_AUTH) {
220e4648aa4SOlga Kornievskaia 		status = -EAGAIN;
2210e65a32cSOlga Kornievskaia 		*restart = true;
222e4648aa4SOlga Kornievskaia 		goto out_cancel;
223c975c209SOlga Kornievskaia 	}
224bc0c9079SOlga Kornievskaia out:
22562164f31SOlga Kornievskaia 	res->write_res.count = copy->count;
22662164f31SOlga Kornievskaia 	memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
22762164f31SOlga Kornievskaia 	status = -copy->error;
22862164f31SOlga Kornievskaia 
22912406025SOlga Kornievskaia out_free:
23062164f31SOlga Kornievskaia 	kfree(copy);
23162164f31SOlga Kornievskaia 	return status;
232e4648aa4SOlga Kornievskaia out_cancel:
233e4648aa4SOlga Kornievskaia 	nfs42_do_offload_cancel_async(dst, &copy->stateid);
23412406025SOlga Kornievskaia 	if (!nfs42_files_from_same_server(src, dst))
23512406025SOlga Kornievskaia 		nfs42_do_offload_cancel_async(src, src_stateid);
23612406025SOlga Kornievskaia 	goto out_free;
23762164f31SOlga Kornievskaia }
23862164f31SOlga Kornievskaia 
2396b8d84e2SOlga Kornievskaia static int process_copy_commit(struct file *dst, loff_t pos_dst,
2406b8d84e2SOlga Kornievskaia 			       struct nfs42_copy_res *res)
2416b8d84e2SOlga Kornievskaia {
2426b8d84e2SOlga Kornievskaia 	struct nfs_commitres cres;
2436b8d84e2SOlga Kornievskaia 	int status = -ENOMEM;
2446b8d84e2SOlga Kornievskaia 
2456b8d84e2SOlga Kornievskaia 	cres.verf = kzalloc(sizeof(struct nfs_writeverf), GFP_NOFS);
2466b8d84e2SOlga Kornievskaia 	if (!cres.verf)
2476b8d84e2SOlga Kornievskaia 		goto out;
2486b8d84e2SOlga Kornievskaia 
2496b8d84e2SOlga Kornievskaia 	status = nfs4_proc_commit(dst, pos_dst, res->write_res.count, &cres);
2506b8d84e2SOlga Kornievskaia 	if (status)
2516b8d84e2SOlga Kornievskaia 		goto out_free;
2526b8d84e2SOlga Kornievskaia 	if (nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
2536b8d84e2SOlga Kornievskaia 				    &cres.verf->verifier)) {
2546b8d84e2SOlga Kornievskaia 		dprintk("commit verf differs from copy verf\n");
2556b8d84e2SOlga Kornievskaia 		status = -EAGAIN;
2566b8d84e2SOlga Kornievskaia 	}
2576b8d84e2SOlga Kornievskaia out_free:
2586b8d84e2SOlga Kornievskaia 	kfree(cres.verf);
2596b8d84e2SOlga Kornievskaia out:
2606b8d84e2SOlga Kornievskaia 	return status;
2616b8d84e2SOlga Kornievskaia }
2626b8d84e2SOlga Kornievskaia 
2639d8cacbfSTrond Myklebust static ssize_t _nfs42_proc_copy(struct file *src,
2642e72448bSAnna Schumaker 				struct nfs_lock_context *src_lock,
2659d8cacbfSTrond Myklebust 				struct file *dst,
2662e72448bSAnna Schumaker 				struct nfs_lock_context *dst_lock,
2679d8cacbfSTrond Myklebust 				struct nfs42_copy_args *args,
2681d38f3f0SOlga Kornievskaia 				struct nfs42_copy_res *res,
2691d38f3f0SOlga Kornievskaia 				struct nl4_server *nss,
2700e65a32cSOlga Kornievskaia 				nfs4_stateid *cnr_stateid,
2710e65a32cSOlga Kornievskaia 				bool *restart)
2722e72448bSAnna Schumaker {
2732e72448bSAnna Schumaker 	struct rpc_message msg = {
2742e72448bSAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY],
2759d8cacbfSTrond Myklebust 		.rpc_argp = args,
2769d8cacbfSTrond Myklebust 		.rpc_resp = res,
2772e72448bSAnna Schumaker 	};
2782e72448bSAnna Schumaker 	struct inode *dst_inode = file_inode(dst);
2790e65a32cSOlga Kornievskaia 	struct inode *src_inode = file_inode(src);
2800e65a32cSOlga Kornievskaia 	struct nfs_server *dst_server = NFS_SERVER(dst_inode);
2810e65a32cSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(src_inode);
2829d8cacbfSTrond Myklebust 	loff_t pos_src = args->src_pos;
2839d8cacbfSTrond Myklebust 	loff_t pos_dst = args->dst_pos;
2849d8cacbfSTrond Myklebust 	size_t count = args->count;
2851ee48bddSOlga Kornievskaia 	ssize_t status;
2862e72448bSAnna Schumaker 
2871d38f3f0SOlga Kornievskaia 	if (nss) {
2881d38f3f0SOlga Kornievskaia 		args->cp_src = nss;
2891d38f3f0SOlga Kornievskaia 		nfs4_stateid_copy(&args->src_stateid, cnr_stateid);
2901d38f3f0SOlga Kornievskaia 	} else {
2911d38f3f0SOlga Kornievskaia 		status = nfs4_set_rw_stateid(&args->src_stateid,
2921d38f3f0SOlga Kornievskaia 				src_lock->open_context, src_lock, FMODE_READ);
293d826e5b8SOlga Kornievskaia 		if (status) {
294d826e5b8SOlga Kornievskaia 			if (status == -EAGAIN)
295d826e5b8SOlga Kornievskaia 				status = -NFS4ERR_BAD_STATEID;
2962e72448bSAnna Schumaker 			return status;
2971d38f3f0SOlga Kornievskaia 		}
298d826e5b8SOlga Kornievskaia 	}
299837bb1d7STrond Myklebust 	status = nfs_filemap_write_and_wait_range(file_inode(src)->i_mapping,
300837bb1d7STrond Myklebust 			pos_src, pos_src + (loff_t)count - 1);
301837bb1d7STrond Myklebust 	if (status)
302837bb1d7STrond Myklebust 		return status;
303837bb1d7STrond Myklebust 
3049d8cacbfSTrond Myklebust 	status = nfs4_set_rw_stateid(&args->dst_stateid, dst_lock->open_context,
3052e72448bSAnna Schumaker 				     dst_lock, FMODE_WRITE);
306d826e5b8SOlga Kornievskaia 	if (status) {
307d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
308d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
3092e72448bSAnna Schumaker 		return status;
310d826e5b8SOlga Kornievskaia 	}
3112e72448bSAnna Schumaker 
312837bb1d7STrond Myklebust 	status = nfs_sync_inode(dst_inode);
313837bb1d7STrond Myklebust 	if (status)
314837bb1d7STrond Myklebust 		return status;
315837bb1d7STrond Myklebust 
31662164f31SOlga Kornievskaia 	res->commit_res.verf = NULL;
31762164f31SOlga Kornievskaia 	if (args->sync) {
31862164f31SOlga Kornievskaia 		res->commit_res.verf =
31962164f31SOlga Kornievskaia 			kzalloc(sizeof(struct nfs_writeverf), GFP_NOFS);
320e0926934SOlga Kornievskaia 		if (!res->commit_res.verf)
321e0926934SOlga Kornievskaia 			return -ENOMEM;
32262164f31SOlga Kornievskaia 	}
3230e65a32cSOlga Kornievskaia 	set_bit(NFS_CLNT_SRC_SSC_COPY_STATE,
3240e65a32cSOlga Kornievskaia 		&src_lock->open_context->state->flags);
325e4648aa4SOlga Kornievskaia 	set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
326e4648aa4SOlga Kornievskaia 		&dst_lock->open_context->state->flags);
327e4648aa4SOlga Kornievskaia 
3280e65a32cSOlga Kornievskaia 	status = nfs4_call_sync(dst_server->client, dst_server, &msg,
3299d8cacbfSTrond Myklebust 				&args->seq_args, &res->seq_res, 0);
3302e72448bSAnna Schumaker 	if (status == -ENOTSUPP)
3310e65a32cSOlga Kornievskaia 		dst_server->caps &= ~NFS_CAP_COPY;
3322e72448bSAnna Schumaker 	if (status)
333e0926934SOlga Kornievskaia 		goto out;
3342e72448bSAnna Schumaker 
33562164f31SOlga Kornievskaia 	if (args->sync &&
33662164f31SOlga Kornievskaia 		nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
337e0926934SOlga Kornievskaia 				    &res->commit_res.verf->verifier)) {
338e0926934SOlga Kornievskaia 		status = -EAGAIN;
339e0926934SOlga Kornievskaia 		goto out;
3402e72448bSAnna Schumaker 	}
3412e72448bSAnna Schumaker 
34262164f31SOlga Kornievskaia 	if (!res->synchronous) {
3430e65a32cSOlga Kornievskaia 		status = handle_async_copy(res, dst_server, src_server, src,
3440e65a32cSOlga Kornievskaia 				dst, &args->src_stateid, restart);
34562164f31SOlga Kornievskaia 		if (status)
34662164f31SOlga Kornievskaia 			return status;
34762164f31SOlga Kornievskaia 	}
34862164f31SOlga Kornievskaia 
3496b8d84e2SOlga Kornievskaia 	if ((!res->synchronous || !args->sync) &&
3506b8d84e2SOlga Kornievskaia 			res->write_res.verifier.committed != NFS_FILE_SYNC) {
3516b8d84e2SOlga Kornievskaia 		status = process_copy_commit(dst, pos_dst, res);
3526b8d84e2SOlga Kornievskaia 		if (status)
3536b8d84e2SOlga Kornievskaia 			return status;
3546b8d84e2SOlga Kornievskaia 	}
3556b8d84e2SOlga Kornievskaia 
3562e72448bSAnna Schumaker 	truncate_pagecache_range(dst_inode, pos_dst,
3579d8cacbfSTrond Myklebust 				 pos_dst + res->write_res.count);
3582e72448bSAnna Schumaker 
359e0926934SOlga Kornievskaia 	status = res->write_res.count;
360e0926934SOlga Kornievskaia out:
36162164f31SOlga Kornievskaia 	if (args->sync)
362e0926934SOlga Kornievskaia 		kfree(res->commit_res.verf);
363e0926934SOlga Kornievskaia 	return status;
3642e72448bSAnna Schumaker }
3652e72448bSAnna Schumaker 
3662e72448bSAnna Schumaker ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
3671d38f3f0SOlga Kornievskaia 			struct file *dst, loff_t pos_dst, size_t count,
3681d38f3f0SOlga Kornievskaia 			struct nl4_server *nss,
36912751010SOlga Kornievskaia 			nfs4_stateid *cnr_stateid, bool sync)
3702e72448bSAnna Schumaker {
3712e72448bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(dst));
3722e72448bSAnna Schumaker 	struct nfs_lock_context *src_lock;
3732e72448bSAnna Schumaker 	struct nfs_lock_context *dst_lock;
3749d8cacbfSTrond Myklebust 	struct nfs42_copy_args args = {
3759d8cacbfSTrond Myklebust 		.src_fh		= NFS_FH(file_inode(src)),
3769d8cacbfSTrond Myklebust 		.src_pos	= pos_src,
3779d8cacbfSTrond Myklebust 		.dst_fh		= NFS_FH(file_inode(dst)),
3789d8cacbfSTrond Myklebust 		.dst_pos	= pos_dst,
3799d8cacbfSTrond Myklebust 		.count		= count,
38012751010SOlga Kornievskaia 		.sync		= sync,
3819d8cacbfSTrond Myklebust 	};
3829d8cacbfSTrond Myklebust 	struct nfs42_copy_res res;
3839d8cacbfSTrond Myklebust 	struct nfs4_exception src_exception = {
3849d8cacbfSTrond Myklebust 		.inode		= file_inode(src),
3859d8cacbfSTrond Myklebust 		.stateid	= &args.src_stateid,
3869d8cacbfSTrond Myklebust 	};
3879d8cacbfSTrond Myklebust 	struct nfs4_exception dst_exception = {
3889d8cacbfSTrond Myklebust 		.inode		= file_inode(dst),
3899d8cacbfSTrond Myklebust 		.stateid	= &args.dst_stateid,
3909d8cacbfSTrond Myklebust 	};
3912e72448bSAnna Schumaker 	ssize_t err, err2;
3920e65a32cSOlga Kornievskaia 	bool restart = false;
3932e72448bSAnna Schumaker 
3942e72448bSAnna Schumaker 	src_lock = nfs_get_lock_context(nfs_file_open_context(src));
3952e72448bSAnna Schumaker 	if (IS_ERR(src_lock))
3962e72448bSAnna Schumaker 		return PTR_ERR(src_lock);
3972e72448bSAnna Schumaker 
3982e72448bSAnna Schumaker 	src_exception.state = src_lock->open_context->state;
3992e72448bSAnna Schumaker 
4002e72448bSAnna Schumaker 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst));
4012e72448bSAnna Schumaker 	if (IS_ERR(dst_lock)) {
4022e72448bSAnna Schumaker 		err = PTR_ERR(dst_lock);
4032e72448bSAnna Schumaker 		goto out_put_src_lock;
4042e72448bSAnna Schumaker 	}
4052e72448bSAnna Schumaker 
4062e72448bSAnna Schumaker 	dst_exception.state = dst_lock->open_context->state;
4072e72448bSAnna Schumaker 
4082e72448bSAnna Schumaker 	do {
409ea8ea737SLinus Torvalds 		inode_lock(file_inode(dst));
4109d8cacbfSTrond Myklebust 		err = _nfs42_proc_copy(src, src_lock,
4119d8cacbfSTrond Myklebust 				dst, dst_lock,
4121d38f3f0SOlga Kornievskaia 				&args, &res,
4130e65a32cSOlga Kornievskaia 				nss, cnr_stateid, &restart);
414ea8ea737SLinus Torvalds 		inode_unlock(file_inode(dst));
4152e72448bSAnna Schumaker 
4169d8cacbfSTrond Myklebust 		if (err >= 0)
4179d8cacbfSTrond Myklebust 			break;
41812406025SOlga Kornievskaia 		if (err == -ENOTSUPP &&
41912406025SOlga Kornievskaia 				nfs42_files_from_same_server(src, dst)) {
4202e72448bSAnna Schumaker 			err = -EOPNOTSUPP;
4212e72448bSAnna Schumaker 			break;
422539f57b3SOlga Kornievskaia 		} else if (err == -EAGAIN) {
4230e65a32cSOlga Kornievskaia 			if (!restart) {
424539f57b3SOlga Kornievskaia 				dst_exception.retry = 1;
425539f57b3SOlga Kornievskaia 				continue;
4260e65a32cSOlga Kornievskaia 			}
4270e65a32cSOlga Kornievskaia 			break;
428539f57b3SOlga Kornievskaia 		} else if (err == -NFS4ERR_OFFLOAD_NO_REQS && !args.sync) {
429539f57b3SOlga Kornievskaia 			args.sync = true;
430e0926934SOlga Kornievskaia 			dst_exception.retry = 1;
431e0926934SOlga Kornievskaia 			continue;
4326b61c969SOlga Kornievskaia 		} else if ((err == -ESTALE ||
43312406025SOlga Kornievskaia 				err == -NFS4ERR_OFFLOAD_DENIED ||
43412406025SOlga Kornievskaia 				err == -ENOTSUPP) &&
4357e350197SOlga Kornievskaia 				!nfs42_files_from_same_server(src, dst)) {
4367e350197SOlga Kornievskaia 			nfs42_do_offload_cancel_async(src, &args.src_stateid);
4377e350197SOlga Kornievskaia 			err = -EOPNOTSUPP;
4387e350197SOlga Kornievskaia 			break;
4392e72448bSAnna Schumaker 		}
4402e72448bSAnna Schumaker 
4412e72448bSAnna Schumaker 		err2 = nfs4_handle_exception(server, err, &src_exception);
4422e72448bSAnna Schumaker 		err  = nfs4_handle_exception(server, err, &dst_exception);
4432e72448bSAnna Schumaker 		if (!err)
4442e72448bSAnna Schumaker 			err = err2;
4452e72448bSAnna Schumaker 	} while (src_exception.retry || dst_exception.retry);
4462e72448bSAnna Schumaker 
4472e72448bSAnna Schumaker 	nfs_put_lock_context(dst_lock);
4482e72448bSAnna Schumaker out_put_src_lock:
4492e72448bSAnna Schumaker 	nfs_put_lock_context(src_lock);
4502e72448bSAnna Schumaker 	return err;
4512e72448bSAnna Schumaker }
4522e72448bSAnna Schumaker 
453c975c209SOlga Kornievskaia struct nfs42_offloadcancel_data {
454c975c209SOlga Kornievskaia 	struct nfs_server *seq_server;
455c975c209SOlga Kornievskaia 	struct nfs42_offload_status_args args;
456c975c209SOlga Kornievskaia 	struct nfs42_offload_status_res res;
457c975c209SOlga Kornievskaia };
458c975c209SOlga Kornievskaia 
459c975c209SOlga Kornievskaia static void nfs42_offload_cancel_prepare(struct rpc_task *task, void *calldata)
460c975c209SOlga Kornievskaia {
461c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
462c975c209SOlga Kornievskaia 
463c975c209SOlga Kornievskaia 	nfs4_setup_sequence(data->seq_server->nfs_client,
464c975c209SOlga Kornievskaia 				&data->args.osa_seq_args,
465c975c209SOlga Kornievskaia 				&data->res.osr_seq_res, task);
466c975c209SOlga Kornievskaia }
467c975c209SOlga Kornievskaia 
468c975c209SOlga Kornievskaia static void nfs42_offload_cancel_done(struct rpc_task *task, void *calldata)
469c975c209SOlga Kornievskaia {
470c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
471c975c209SOlga Kornievskaia 
472c975c209SOlga Kornievskaia 	nfs41_sequence_done(task, &data->res.osr_seq_res);
473c975c209SOlga Kornievskaia 	if (task->tk_status &&
474c975c209SOlga Kornievskaia 		nfs4_async_handle_error(task, data->seq_server, NULL,
475c975c209SOlga Kornievskaia 			NULL) == -EAGAIN)
476c975c209SOlga Kornievskaia 		rpc_restart_call_prepare(task);
477c975c209SOlga Kornievskaia }
478c975c209SOlga Kornievskaia 
479c975c209SOlga Kornievskaia static void nfs42_free_offloadcancel_data(void *data)
480c975c209SOlga Kornievskaia {
481c975c209SOlga Kornievskaia 	kfree(data);
482c975c209SOlga Kornievskaia }
483c975c209SOlga Kornievskaia 
484c975c209SOlga Kornievskaia static const struct rpc_call_ops nfs42_offload_cancel_ops = {
485c975c209SOlga Kornievskaia 	.rpc_call_prepare = nfs42_offload_cancel_prepare,
486c975c209SOlga Kornievskaia 	.rpc_call_done = nfs42_offload_cancel_done,
487c975c209SOlga Kornievskaia 	.rpc_release = nfs42_free_offloadcancel_data,
488c975c209SOlga Kornievskaia };
489c975c209SOlga Kornievskaia 
490c975c209SOlga Kornievskaia static int nfs42_do_offload_cancel_async(struct file *dst,
491c975c209SOlga Kornievskaia 					 nfs4_stateid *stateid)
492c975c209SOlga Kornievskaia {
493c975c209SOlga Kornievskaia 	struct nfs_server *dst_server = NFS_SERVER(file_inode(dst));
494c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = NULL;
495c975c209SOlga Kornievskaia 	struct nfs_open_context *ctx = nfs_file_open_context(dst);
496c975c209SOlga Kornievskaia 	struct rpc_task *task;
497c975c209SOlga Kornievskaia 	struct rpc_message msg = {
498c975c209SOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OFFLOAD_CANCEL],
499c975c209SOlga Kornievskaia 		.rpc_cred = ctx->cred,
500c975c209SOlga Kornievskaia 	};
501c975c209SOlga Kornievskaia 	struct rpc_task_setup task_setup_data = {
502c975c209SOlga Kornievskaia 		.rpc_client = dst_server->client,
503c975c209SOlga Kornievskaia 		.rpc_message = &msg,
504c975c209SOlga Kornievskaia 		.callback_ops = &nfs42_offload_cancel_ops,
505c975c209SOlga Kornievskaia 		.workqueue = nfsiod_workqueue,
506c975c209SOlga Kornievskaia 		.flags = RPC_TASK_ASYNC,
507c975c209SOlga Kornievskaia 	};
508c975c209SOlga Kornievskaia 	int status;
509c975c209SOlga Kornievskaia 
510c975c209SOlga Kornievskaia 	if (!(dst_server->caps & NFS_CAP_OFFLOAD_CANCEL))
511c975c209SOlga Kornievskaia 		return -EOPNOTSUPP;
512c975c209SOlga Kornievskaia 
513c975c209SOlga Kornievskaia 	data = kzalloc(sizeof(struct nfs42_offloadcancel_data), GFP_NOFS);
514c975c209SOlga Kornievskaia 	if (data == NULL)
515c975c209SOlga Kornievskaia 		return -ENOMEM;
516c975c209SOlga Kornievskaia 
517c975c209SOlga Kornievskaia 	data->seq_server = dst_server;
518c975c209SOlga Kornievskaia 	data->args.osa_src_fh = NFS_FH(file_inode(dst));
519c975c209SOlga Kornievskaia 	memcpy(&data->args.osa_stateid, stateid,
520c975c209SOlga Kornievskaia 		sizeof(data->args.osa_stateid));
521c975c209SOlga Kornievskaia 	msg.rpc_argp = &data->args;
522c975c209SOlga Kornievskaia 	msg.rpc_resp = &data->res;
523c975c209SOlga Kornievskaia 	task_setup_data.callback_data = data;
524c975c209SOlga Kornievskaia 	nfs4_init_sequence(&data->args.osa_seq_args, &data->res.osr_seq_res,
525c975c209SOlga Kornievskaia 			   1, 0);
526c975c209SOlga Kornievskaia 	task = rpc_run_task(&task_setup_data);
527c975c209SOlga Kornievskaia 	if (IS_ERR(task))
528c975c209SOlga Kornievskaia 		return PTR_ERR(task);
529c975c209SOlga Kornievskaia 	status = rpc_wait_for_completion_task(task);
530c975c209SOlga Kornievskaia 	if (status == -ENOTSUPP)
531c975c209SOlga Kornievskaia 		dst_server->caps &= ~NFS_CAP_OFFLOAD_CANCEL;
532c975c209SOlga Kornievskaia 	rpc_put_task(task);
533c975c209SOlga Kornievskaia 	return status;
534c975c209SOlga Kornievskaia }
535c975c209SOlga Kornievskaia 
53600030104SYueHaibing static int _nfs42_proc_copy_notify(struct file *src, struct file *dst,
5370491567bSOlga Kornievskaia 				   struct nfs42_copy_notify_args *args,
5380491567bSOlga Kornievskaia 				   struct nfs42_copy_notify_res *res)
5390491567bSOlga Kornievskaia {
5400491567bSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
5410491567bSOlga Kornievskaia 	struct rpc_message msg = {
5420491567bSOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY_NOTIFY],
5430491567bSOlga Kornievskaia 		.rpc_argp = args,
5440491567bSOlga Kornievskaia 		.rpc_resp = res,
5450491567bSOlga Kornievskaia 	};
5460491567bSOlga Kornievskaia 	int status;
5470491567bSOlga Kornievskaia 	struct nfs_open_context *ctx;
5480491567bSOlga Kornievskaia 	struct nfs_lock_context *l_ctx;
5490491567bSOlga Kornievskaia 
5500491567bSOlga Kornievskaia 	ctx = get_nfs_open_context(nfs_file_open_context(src));
5510491567bSOlga Kornievskaia 	l_ctx = nfs_get_lock_context(ctx);
5520491567bSOlga Kornievskaia 	if (IS_ERR(l_ctx))
5530491567bSOlga Kornievskaia 		return PTR_ERR(l_ctx);
5540491567bSOlga Kornievskaia 
5550491567bSOlga Kornievskaia 	status = nfs4_set_rw_stateid(&args->cna_src_stateid, ctx, l_ctx,
5560491567bSOlga Kornievskaia 				     FMODE_READ);
5570491567bSOlga Kornievskaia 	nfs_put_lock_context(l_ctx);
558d826e5b8SOlga Kornievskaia 	if (status) {
559d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
560d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
5610491567bSOlga Kornievskaia 		return status;
562d826e5b8SOlga Kornievskaia 	}
5630491567bSOlga Kornievskaia 
5640491567bSOlga Kornievskaia 	status = nfs4_call_sync(src_server->client, src_server, &msg,
5650491567bSOlga Kornievskaia 				&args->cna_seq_args, &res->cnr_seq_res, 0);
5660491567bSOlga Kornievskaia 	if (status == -ENOTSUPP)
5670491567bSOlga Kornievskaia 		src_server->caps &= ~NFS_CAP_COPY_NOTIFY;
5680491567bSOlga Kornievskaia 
5690491567bSOlga Kornievskaia 	put_nfs_open_context(nfs_file_open_context(src));
5700491567bSOlga Kornievskaia 	return status;
5710491567bSOlga Kornievskaia }
5720491567bSOlga Kornievskaia 
5730491567bSOlga Kornievskaia int nfs42_proc_copy_notify(struct file *src, struct file *dst,
5740491567bSOlga Kornievskaia 				struct nfs42_copy_notify_res *res)
5750491567bSOlga Kornievskaia {
5760491567bSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
5770491567bSOlga Kornievskaia 	struct nfs42_copy_notify_args *args;
5780491567bSOlga Kornievskaia 	struct nfs4_exception exception = {
5790491567bSOlga Kornievskaia 		.inode = file_inode(src),
5800491567bSOlga Kornievskaia 	};
5810491567bSOlga Kornievskaia 	int status;
5820491567bSOlga Kornievskaia 
5830491567bSOlga Kornievskaia 	if (!(src_server->caps & NFS_CAP_COPY_NOTIFY))
5840491567bSOlga Kornievskaia 		return -EOPNOTSUPP;
5850491567bSOlga Kornievskaia 
5860491567bSOlga Kornievskaia 	args = kzalloc(sizeof(struct nfs42_copy_notify_args), GFP_NOFS);
5870491567bSOlga Kornievskaia 	if (args == NULL)
5880491567bSOlga Kornievskaia 		return -ENOMEM;
5890491567bSOlga Kornievskaia 
5900491567bSOlga Kornievskaia 	args->cna_src_fh  = NFS_FH(file_inode(src)),
5910491567bSOlga Kornievskaia 	args->cna_dst.nl4_type = NL4_NETADDR;
5920491567bSOlga Kornievskaia 	nfs42_set_netaddr(dst, &args->cna_dst.u.nl4_addr);
5930491567bSOlga Kornievskaia 	exception.stateid = &args->cna_src_stateid;
5940491567bSOlga Kornievskaia 
5950491567bSOlga Kornievskaia 	do {
5960491567bSOlga Kornievskaia 		status = _nfs42_proc_copy_notify(src, dst, args, res);
5970491567bSOlga Kornievskaia 		if (status == -ENOTSUPP) {
5980491567bSOlga Kornievskaia 			status = -EOPNOTSUPP;
5990491567bSOlga Kornievskaia 			goto out;
6000491567bSOlga Kornievskaia 		}
6010491567bSOlga Kornievskaia 		status = nfs4_handle_exception(src_server, status, &exception);
6020491567bSOlga Kornievskaia 	} while (exception.retry);
6030491567bSOlga Kornievskaia 
6040491567bSOlga Kornievskaia out:
6050491567bSOlga Kornievskaia 	kfree(args);
6060491567bSOlga Kornievskaia 	return status;
6070491567bSOlga Kornievskaia }
6080491567bSOlga Kornievskaia 
6094bdf87ebSChristoph Hellwig static loff_t _nfs42_proc_llseek(struct file *filep,
6104bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, int whence)
6111c6dcbe5SAnna Schumaker {
6121c6dcbe5SAnna Schumaker 	struct inode *inode = file_inode(filep);
6131c6dcbe5SAnna Schumaker 	struct nfs42_seek_args args = {
6141c6dcbe5SAnna Schumaker 		.sa_fh		= NFS_FH(inode),
6151c6dcbe5SAnna Schumaker 		.sa_offset	= offset,
6161c6dcbe5SAnna Schumaker 		.sa_what	= (whence == SEEK_HOLE) ?
6171c6dcbe5SAnna Schumaker 					NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA,
6181c6dcbe5SAnna Schumaker 	};
6191c6dcbe5SAnna Schumaker 	struct nfs42_seek_res res;
6201c6dcbe5SAnna Schumaker 	struct rpc_message msg = {
6211c6dcbe5SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK],
6221c6dcbe5SAnna Schumaker 		.rpc_argp = &args,
6231c6dcbe5SAnna Schumaker 		.rpc_resp = &res,
6241c6dcbe5SAnna Schumaker 	};
6251c6dcbe5SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
6261c6dcbe5SAnna Schumaker 	int status;
6271c6dcbe5SAnna Schumaker 
628878ffa9fSAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_SEEK))
6291c6dcbe5SAnna Schumaker 		return -ENOTSUPP;
6301c6dcbe5SAnna Schumaker 
6314bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context,
6324bdf87ebSChristoph Hellwig 			lock, FMODE_READ);
633d826e5b8SOlga Kornievskaia 	if (status) {
634d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
635d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
6361c6dcbe5SAnna Schumaker 		return status;
637d826e5b8SOlga Kornievskaia 	}
6381c6dcbe5SAnna Schumaker 
639e95fc4a0STrond Myklebust 	status = nfs_filemap_write_and_wait_range(inode->i_mapping,
640e95fc4a0STrond Myklebust 			offset, LLONG_MAX);
641e95fc4a0STrond Myklebust 	if (status)
642e95fc4a0STrond Myklebust 		return status;
643e95fc4a0STrond Myklebust 
6441c6dcbe5SAnna Schumaker 	status = nfs4_call_sync(server->client, server, &msg,
6451c6dcbe5SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
6461c6dcbe5SAnna Schumaker 	if (status == -ENOTSUPP)
6471c6dcbe5SAnna Schumaker 		server->caps &= ~NFS_CAP_SEEK;
6481c6dcbe5SAnna Schumaker 	if (status)
6491c6dcbe5SAnna Schumaker 		return status;
6501c6dcbe5SAnna Schumaker 
6511c6dcbe5SAnna Schumaker 	return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes);
6521c6dcbe5SAnna Schumaker }
653be3a5d23STrond Myklebust 
654bdcc2cd1SJ. Bruce Fields loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
655bdcc2cd1SJ. Bruce Fields {
656bdcc2cd1SJ. Bruce Fields 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
657bdcc2cd1SJ. Bruce Fields 	struct nfs4_exception exception = { };
6584bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
659306a5549SJ. Bruce Fields 	loff_t err;
660bdcc2cd1SJ. Bruce Fields 
6614bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
6624bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
6634bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
6644bdf87ebSChristoph Hellwig 
6654bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
6664bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
6674bdf87ebSChristoph Hellwig 
668bdcc2cd1SJ. Bruce Fields 	do {
6694bdf87ebSChristoph Hellwig 		err = _nfs42_proc_llseek(filep, lock, offset, whence);
670306a5549SJ. Bruce Fields 		if (err >= 0)
671306a5549SJ. Bruce Fields 			break;
6724bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
6734bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
6744bdf87ebSChristoph Hellwig 			break;
6754bdf87ebSChristoph Hellwig 		}
676bdcc2cd1SJ. Bruce Fields 		err = nfs4_handle_exception(server, err, &exception);
677bdcc2cd1SJ. Bruce Fields 	} while (exception.retry);
678bdcc2cd1SJ. Bruce Fields 
6794bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
680bdcc2cd1SJ. Bruce Fields 	return err;
681bdcc2cd1SJ. Bruce Fields }
682bdcc2cd1SJ. Bruce Fields 
683bdcc2cd1SJ. Bruce Fields 
6841b4a4bd8SPeng Tao static void
6851b4a4bd8SPeng Tao nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata)
6861b4a4bd8SPeng Tao {
6871b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
6889a0fe867STrond Myklebust 	struct inode *inode = data->inode;
6899a0fe867STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
6909a0fe867STrond Myklebust 	struct pnfs_layout_hdr *lo;
6911b4a4bd8SPeng Tao 
6929a0fe867STrond Myklebust 	spin_lock(&inode->i_lock);
6939a0fe867STrond Myklebust 	lo = NFS_I(inode)->layout;
6949a0fe867STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
6959a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
6969a0fe867STrond Myklebust 		rpc_exit(task, 0);
6979a0fe867STrond Myklebust 		return;
6989a0fe867STrond Myklebust 	}
6999a0fe867STrond Myklebust 	nfs4_stateid_copy(&data->args.stateid, &lo->plh_stateid);
7009a0fe867STrond Myklebust 	spin_unlock(&inode->i_lock);
7016de7e12fSAnna Schumaker 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
7026de7e12fSAnna Schumaker 			    &data->res.seq_res, task);
7031b4a4bd8SPeng Tao }
7041b4a4bd8SPeng Tao 
7051b4a4bd8SPeng Tao static void
7061b4a4bd8SPeng Tao nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
7071b4a4bd8SPeng Tao {
7081b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
70968d264cfSPeng Tao 	struct inode *inode = data->inode;
71068d264cfSPeng Tao 	struct pnfs_layout_hdr *lo;
7111b4a4bd8SPeng Tao 
7121b4a4bd8SPeng Tao 	if (!nfs4_sequence_done(task, &data->res.seq_res))
7131b4a4bd8SPeng Tao 		return;
7141b4a4bd8SPeng Tao 
7156c5a0d89STrond Myklebust 	switch (task->tk_status) {
7166c5a0d89STrond Myklebust 	case 0:
7176c5a0d89STrond Myklebust 		break;
718cf61eb26STrond Myklebust 	case -NFS4ERR_BADHANDLE:
719cf61eb26STrond Myklebust 	case -ESTALE:
720cf61eb26STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
721cf61eb26STrond Myklebust 		break;
72268d264cfSPeng Tao 	case -NFS4ERR_EXPIRED:
723206b3bb5STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
724206b3bb5STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
72568d264cfSPeng Tao 	case -NFS4ERR_STALE_STATEID:
72668d264cfSPeng Tao 	case -NFS4ERR_BAD_STATEID:
72768d264cfSPeng Tao 		spin_lock(&inode->i_lock);
72868d264cfSPeng Tao 		lo = NFS_I(inode)->layout;
7299a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
7309a0fe867STrond Myklebust 		    nfs4_stateid_match(&data->args.stateid,
73168d264cfSPeng Tao 					     &lo->plh_stateid)) {
73268d264cfSPeng Tao 			LIST_HEAD(head);
73368d264cfSPeng Tao 
73468d264cfSPeng Tao 			/*
73568d264cfSPeng Tao 			 * Mark the bad layout state as invalid, then retry
73668d264cfSPeng Tao 			 * with the current stateid.
73768d264cfSPeng Tao 			 */
7385f46be04STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
73968d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
74068d264cfSPeng Tao 			pnfs_free_lseg_list(&head);
7411f18b82cSTrond Myklebust 			nfs_commit_inode(inode, 0);
74268d264cfSPeng Tao 		} else
74368d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
74468d264cfSPeng Tao 		break;
7459a0fe867STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
7469a0fe867STrond Myklebust 		spin_lock(&inode->i_lock);
7479a0fe867STrond Myklebust 		lo = NFS_I(inode)->layout;
7489a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
7499a0fe867STrond Myklebust 		    nfs4_stateid_match_other(&data->args.stateid,
7509a0fe867STrond Myklebust 					&lo->plh_stateid)) {
7519a0fe867STrond Myklebust 			/* Do we need to delay before resending? */
7529a0fe867STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
7539a0fe867STrond Myklebust 						&data->args.stateid))
7549a0fe867STrond Myklebust 				rpc_delay(task, HZ);
7559a0fe867STrond Myklebust 			rpc_restart_call_prepare(task);
7569a0fe867STrond Myklebust 		}
7579a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
7589a0fe867STrond Myklebust 		break;
7596c5a0d89STrond Myklebust 	case -ENOTSUPP:
7606c5a0d89STrond Myklebust 	case -EOPNOTSUPP:
76168d264cfSPeng Tao 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
7621b4a4bd8SPeng Tao 	}
7636c5a0d89STrond Myklebust }
7641b4a4bd8SPeng Tao 
7651b4a4bd8SPeng Tao static void
7661b4a4bd8SPeng Tao nfs42_layoutstat_release(void *calldata)
7671b4a4bd8SPeng Tao {
7681b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
769422c93c8STrond Myklebust 	struct nfs42_layoutstat_devinfo *devinfo = data->args.devinfo;
770422c93c8STrond Myklebust 	int i;
7718733408dSPeng Tao 
772422c93c8STrond Myklebust 	for (i = 0; i < data->args.num_dev; i++) {
773422c93c8STrond Myklebust 		if (devinfo[i].ld_private.ops && devinfo[i].ld_private.ops->free)
774422c93c8STrond Myklebust 			devinfo[i].ld_private.ops->free(&devinfo[i].ld_private);
775422c93c8STrond Myklebust 	}
7761b4a4bd8SPeng Tao 
7771b4a4bd8SPeng Tao 	pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout);
7781bfe3b25SPeng Tao 	smp_mb__before_atomic();
7791bfe3b25SPeng Tao 	clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags);
7801bfe3b25SPeng Tao 	smp_mb__after_atomic();
7811b4a4bd8SPeng Tao 	nfs_iput_and_deactive(data->inode);
7821b4a4bd8SPeng Tao 	kfree(data->args.devinfo);
7831b4a4bd8SPeng Tao 	kfree(data);
7841b4a4bd8SPeng Tao }
7851b4a4bd8SPeng Tao 
786be3a5d23STrond Myklebust static const struct rpc_call_ops nfs42_layoutstat_ops = {
7871b4a4bd8SPeng Tao 	.rpc_call_prepare = nfs42_layoutstat_prepare,
7881b4a4bd8SPeng Tao 	.rpc_call_done = nfs42_layoutstat_done,
7891b4a4bd8SPeng Tao 	.rpc_release = nfs42_layoutstat_release,
790be3a5d23STrond Myklebust };
791be3a5d23STrond Myklebust 
792be3a5d23STrond Myklebust int nfs42_proc_layoutstats_generic(struct nfs_server *server,
793be3a5d23STrond Myklebust 				   struct nfs42_layoutstat_data *data)
794be3a5d23STrond Myklebust {
795be3a5d23STrond Myklebust 	struct rpc_message msg = {
796be3a5d23STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS],
797be3a5d23STrond Myklebust 		.rpc_argp = &data->args,
798be3a5d23STrond Myklebust 		.rpc_resp = &data->res,
799be3a5d23STrond Myklebust 	};
800be3a5d23STrond Myklebust 	struct rpc_task_setup task_setup = {
801be3a5d23STrond Myklebust 		.rpc_client = server->client,
802be3a5d23STrond Myklebust 		.rpc_message = &msg,
803be3a5d23STrond Myklebust 		.callback_ops = &nfs42_layoutstat_ops,
804be3a5d23STrond Myklebust 		.callback_data = data,
805be3a5d23STrond Myklebust 		.flags = RPC_TASK_ASYNC,
806be3a5d23STrond Myklebust 	};
807be3a5d23STrond Myklebust 	struct rpc_task *task;
808be3a5d23STrond Myklebust 
8091b4a4bd8SPeng Tao 	data->inode = nfs_igrab_and_active(data->args.inode);
8101b4a4bd8SPeng Tao 	if (!data->inode) {
8111b4a4bd8SPeng Tao 		nfs42_layoutstat_release(data);
8121b4a4bd8SPeng Tao 		return -EAGAIN;
8131b4a4bd8SPeng Tao 	}
814fba83f34SAnna Schumaker 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
815be3a5d23STrond Myklebust 	task = rpc_run_task(&task_setup);
816be3a5d23STrond Myklebust 	if (IS_ERR(task))
817be3a5d23STrond Myklebust 		return PTR_ERR(task);
8183f807e5aSJeff Layton 	rpc_put_task(task);
819be3a5d23STrond Myklebust 	return 0;
820be3a5d23STrond Myklebust }
821e5341f3aSPeng Tao 
8223eb86093STrond Myklebust static struct nfs42_layouterror_data *
8233eb86093STrond Myklebust nfs42_alloc_layouterror_data(struct pnfs_layout_segment *lseg, gfp_t gfp_flags)
8243eb86093STrond Myklebust {
8253eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
8263eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
8273eb86093STrond Myklebust 
8283eb86093STrond Myklebust 	data = kzalloc(sizeof(*data), gfp_flags);
8293eb86093STrond Myklebust 	if (data) {
8303eb86093STrond Myklebust 		data->args.inode = data->inode = nfs_igrab_and_active(inode);
8313eb86093STrond Myklebust 		if (data->inode) {
8323eb86093STrond Myklebust 			data->lseg = pnfs_get_lseg(lseg);
8333eb86093STrond Myklebust 			if (data->lseg)
8343eb86093STrond Myklebust 				return data;
8353eb86093STrond Myklebust 			nfs_iput_and_deactive(data->inode);
8363eb86093STrond Myklebust 		}
8373eb86093STrond Myklebust 		kfree(data);
8383eb86093STrond Myklebust 	}
8393eb86093STrond Myklebust 	return NULL;
8403eb86093STrond Myklebust }
8413eb86093STrond Myklebust 
8423eb86093STrond Myklebust static void
8433eb86093STrond Myklebust nfs42_free_layouterror_data(struct nfs42_layouterror_data *data)
8443eb86093STrond Myklebust {
8453eb86093STrond Myklebust 	pnfs_put_lseg(data->lseg);
8463eb86093STrond Myklebust 	nfs_iput_and_deactive(data->inode);
8473eb86093STrond Myklebust 	kfree(data);
8483eb86093STrond Myklebust }
8493eb86093STrond Myklebust 
8503eb86093STrond Myklebust static void
8513eb86093STrond Myklebust nfs42_layouterror_prepare(struct rpc_task *task, void *calldata)
8523eb86093STrond Myklebust {
8533eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
8543eb86093STrond Myklebust 	struct inode *inode = data->inode;
8553eb86093STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
8563eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
8573eb86093STrond Myklebust 	unsigned i;
8583eb86093STrond Myklebust 
8593eb86093STrond Myklebust 	spin_lock(&inode->i_lock);
8603eb86093STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
8613eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
8623eb86093STrond Myklebust 		rpc_exit(task, 0);
8633eb86093STrond Myklebust 		return;
8643eb86093STrond Myklebust 	}
8653eb86093STrond Myklebust 	for (i = 0; i < data->args.num_errors; i++)
8663eb86093STrond Myklebust 		nfs4_stateid_copy(&data->args.errors[i].stateid,
8673eb86093STrond Myklebust 				&lo->plh_stateid);
8683eb86093STrond Myklebust 	spin_unlock(&inode->i_lock);
8693eb86093STrond Myklebust 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
8703eb86093STrond Myklebust 			    &data->res.seq_res, task);
8713eb86093STrond Myklebust }
8723eb86093STrond Myklebust 
8733eb86093STrond Myklebust static void
8743eb86093STrond Myklebust nfs42_layouterror_done(struct rpc_task *task, void *calldata)
8753eb86093STrond Myklebust {
8763eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
8773eb86093STrond Myklebust 	struct inode *inode = data->inode;
8783eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
8793eb86093STrond Myklebust 
8803eb86093STrond Myklebust 	if (!nfs4_sequence_done(task, &data->res.seq_res))
8813eb86093STrond Myklebust 		return;
8823eb86093STrond Myklebust 
8833eb86093STrond Myklebust 	switch (task->tk_status) {
8843eb86093STrond Myklebust 	case 0:
8853eb86093STrond Myklebust 		break;
8863eb86093STrond Myklebust 	case -NFS4ERR_BADHANDLE:
8873eb86093STrond Myklebust 	case -ESTALE:
8883eb86093STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
8893eb86093STrond Myklebust 		break;
8903eb86093STrond Myklebust 	case -NFS4ERR_EXPIRED:
8913eb86093STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
8923eb86093STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
8933eb86093STrond Myklebust 	case -NFS4ERR_STALE_STATEID:
8943eb86093STrond Myklebust 	case -NFS4ERR_BAD_STATEID:
8953eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
8963eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
8973eb86093STrond Myklebust 		    nfs4_stateid_match(&data->args.errors[0].stateid,
8983eb86093STrond Myklebust 					     &lo->plh_stateid)) {
8993eb86093STrond Myklebust 			LIST_HEAD(head);
9003eb86093STrond Myklebust 
9013eb86093STrond Myklebust 			/*
9023eb86093STrond Myklebust 			 * Mark the bad layout state as invalid, then retry
9033eb86093STrond Myklebust 			 * with the current stateid.
9043eb86093STrond Myklebust 			 */
9053eb86093STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
9063eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
9073eb86093STrond Myklebust 			pnfs_free_lseg_list(&head);
9083eb86093STrond Myklebust 			nfs_commit_inode(inode, 0);
9093eb86093STrond Myklebust 		} else
9103eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
9113eb86093STrond Myklebust 		break;
9123eb86093STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
9133eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
9143eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
9153eb86093STrond Myklebust 		    nfs4_stateid_match_other(&data->args.errors[0].stateid,
9163eb86093STrond Myklebust 					&lo->plh_stateid)) {
9173eb86093STrond Myklebust 			/* Do we need to delay before resending? */
9183eb86093STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
9193eb86093STrond Myklebust 						&data->args.errors[0].stateid))
9203eb86093STrond Myklebust 				rpc_delay(task, HZ);
9213eb86093STrond Myklebust 			rpc_restart_call_prepare(task);
9223eb86093STrond Myklebust 		}
9233eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
9243eb86093STrond Myklebust 		break;
9253eb86093STrond Myklebust 	case -ENOTSUPP:
9263eb86093STrond Myklebust 	case -EOPNOTSUPP:
9273eb86093STrond Myklebust 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTERROR;
9283eb86093STrond Myklebust 	}
9293eb86093STrond Myklebust }
9303eb86093STrond Myklebust 
9313eb86093STrond Myklebust static void
9323eb86093STrond Myklebust nfs42_layouterror_release(void *calldata)
9333eb86093STrond Myklebust {
9343eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
9353eb86093STrond Myklebust 
9363eb86093STrond Myklebust 	nfs42_free_layouterror_data(data);
9373eb86093STrond Myklebust }
9383eb86093STrond Myklebust 
9393eb86093STrond Myklebust static const struct rpc_call_ops nfs42_layouterror_ops = {
9403eb86093STrond Myklebust 	.rpc_call_prepare = nfs42_layouterror_prepare,
9413eb86093STrond Myklebust 	.rpc_call_done = nfs42_layouterror_done,
9423eb86093STrond Myklebust 	.rpc_release = nfs42_layouterror_release,
9433eb86093STrond Myklebust };
9443eb86093STrond Myklebust 
9453eb86093STrond Myklebust int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
9463eb86093STrond Myklebust 		const struct nfs42_layout_error *errors, size_t n)
9473eb86093STrond Myklebust {
9483eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
9493eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
9503eb86093STrond Myklebust 	struct rpc_task *task;
9513eb86093STrond Myklebust 	struct rpc_message msg = {
9523eb86093STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTERROR],
9533eb86093STrond Myklebust 	};
9543eb86093STrond Myklebust 	struct rpc_task_setup task_setup = {
9553eb86093STrond Myklebust 		.rpc_message = &msg,
9563eb86093STrond Myklebust 		.callback_ops = &nfs42_layouterror_ops,
9573eb86093STrond Myklebust 		.flags = RPC_TASK_ASYNC,
9583eb86093STrond Myklebust 	};
9593eb86093STrond Myklebust 	unsigned int i;
9603eb86093STrond Myklebust 
9613eb86093STrond Myklebust 	if (!nfs_server_capable(inode, NFS_CAP_LAYOUTERROR))
9623eb86093STrond Myklebust 		return -EOPNOTSUPP;
9633eb86093STrond Myklebust 	if (n > NFS42_LAYOUTERROR_MAX)
9643eb86093STrond Myklebust 		return -EINVAL;
9653eb86093STrond Myklebust 	data = nfs42_alloc_layouterror_data(lseg, GFP_NOFS);
9663eb86093STrond Myklebust 	if (!data)
9673eb86093STrond Myklebust 		return -ENOMEM;
9683eb86093STrond Myklebust 	for (i = 0; i < n; i++) {
9693eb86093STrond Myklebust 		data->args.errors[i] = errors[i];
9703eb86093STrond Myklebust 		data->args.num_errors++;
9713eb86093STrond Myklebust 		data->res.num_errors++;
9723eb86093STrond Myklebust 	}
9733eb86093STrond Myklebust 	msg.rpc_argp = &data->args;
9743eb86093STrond Myklebust 	msg.rpc_resp = &data->res;
9753eb86093STrond Myklebust 	task_setup.callback_data = data;
9763eb86093STrond Myklebust 	task_setup.rpc_client = NFS_SERVER(inode)->client;
9773eb86093STrond Myklebust 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
9783eb86093STrond Myklebust 	task = rpc_run_task(&task_setup);
9793eb86093STrond Myklebust 	if (IS_ERR(task))
9803eb86093STrond Myklebust 		return PTR_ERR(task);
9813eb86093STrond Myklebust 	rpc_put_task(task);
9823eb86093STrond Myklebust 	return 0;
9833eb86093STrond Myklebust }
9843eb86093STrond Myklebust EXPORT_SYMBOL_GPL(nfs42_proc_layouterror);
9853eb86093STrond Myklebust 
986e5341f3aSPeng Tao static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
9874bdf87ebSChristoph Hellwig 		struct file *dst_f, struct nfs_lock_context *src_lock,
9884bdf87ebSChristoph Hellwig 		struct nfs_lock_context *dst_lock, loff_t src_offset,
989e5341f3aSPeng Tao 		loff_t dst_offset, loff_t count)
990e5341f3aSPeng Tao {
991e5341f3aSPeng Tao 	struct inode *src_inode = file_inode(src_f);
992e5341f3aSPeng Tao 	struct inode *dst_inode = file_inode(dst_f);
993e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(dst_inode);
994e5341f3aSPeng Tao 	struct nfs42_clone_args args = {
995e5341f3aSPeng Tao 		.src_fh = NFS_FH(src_inode),
996e5341f3aSPeng Tao 		.dst_fh = NFS_FH(dst_inode),
997e5341f3aSPeng Tao 		.src_offset = src_offset,
998e5341f3aSPeng Tao 		.dst_offset = dst_offset,
9999494b2ceSChristoph Hellwig 		.count = count,
1000e5341f3aSPeng Tao 		.dst_bitmask = server->cache_consistency_bitmask,
1001e5341f3aSPeng Tao 	};
1002e5341f3aSPeng Tao 	struct nfs42_clone_res res = {
1003e5341f3aSPeng Tao 		.server	= server,
1004e5341f3aSPeng Tao 	};
1005e5341f3aSPeng Tao 	int status;
1006e5341f3aSPeng Tao 
1007e5341f3aSPeng Tao 	msg->rpc_argp = &args;
1008e5341f3aSPeng Tao 	msg->rpc_resp = &res;
1009e5341f3aSPeng Tao 
10104bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context,
10114bdf87ebSChristoph Hellwig 			src_lock, FMODE_READ);
1012d826e5b8SOlga Kornievskaia 	if (status) {
1013d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
1014d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
1015e5341f3aSPeng Tao 		return status;
1016d826e5b8SOlga Kornievskaia 	}
10174bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
10184bdf87ebSChristoph Hellwig 			dst_lock, FMODE_WRITE);
1019d826e5b8SOlga Kornievskaia 	if (status) {
1020d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
1021d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
1022e5341f3aSPeng Tao 		return status;
1023d826e5b8SOlga Kornievskaia 	}
1024e5341f3aSPeng Tao 
1025e5341f3aSPeng Tao 	res.dst_fattr = nfs_alloc_fattr();
1026e5341f3aSPeng Tao 	if (!res.dst_fattr)
1027e5341f3aSPeng Tao 		return -ENOMEM;
1028e5341f3aSPeng Tao 
1029e5341f3aSPeng Tao 	status = nfs4_call_sync(server->client, server, msg,
1030e5341f3aSPeng Tao 				&args.seq_args, &res.seq_res, 0);
1031e5341f3aSPeng Tao 	if (status == 0)
1032e5341f3aSPeng Tao 		status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
1033e5341f3aSPeng Tao 
1034e5341f3aSPeng Tao 	kfree(res.dst_fattr);
1035e5341f3aSPeng Tao 	return status;
1036e5341f3aSPeng Tao }
1037e5341f3aSPeng Tao 
1038e5341f3aSPeng Tao int nfs42_proc_clone(struct file *src_f, struct file *dst_f,
1039e5341f3aSPeng Tao 		     loff_t src_offset, loff_t dst_offset, loff_t count)
1040e5341f3aSPeng Tao {
1041e5341f3aSPeng Tao 	struct rpc_message msg = {
1042e5341f3aSPeng Tao 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE],
1043e5341f3aSPeng Tao 	};
1044e5341f3aSPeng Tao 	struct inode *inode = file_inode(src_f);
1045e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(file_inode(src_f));
10464bdf87ebSChristoph Hellwig 	struct nfs_lock_context *src_lock;
10474bdf87ebSChristoph Hellwig 	struct nfs_lock_context *dst_lock;
10484bdf87ebSChristoph Hellwig 	struct nfs4_exception src_exception = { };
10494bdf87ebSChristoph Hellwig 	struct nfs4_exception dst_exception = { };
10504bdf87ebSChristoph Hellwig 	int err, err2;
1051e5341f3aSPeng Tao 
1052e5341f3aSPeng Tao 	if (!nfs_server_capable(inode, NFS_CAP_CLONE))
1053e5341f3aSPeng Tao 		return -EOPNOTSUPP;
1054e5341f3aSPeng Tao 
10554bdf87ebSChristoph Hellwig 	src_lock = nfs_get_lock_context(nfs_file_open_context(src_f));
10564bdf87ebSChristoph Hellwig 	if (IS_ERR(src_lock))
10574bdf87ebSChristoph Hellwig 		return PTR_ERR(src_lock);
10584bdf87ebSChristoph Hellwig 
10594bdf87ebSChristoph Hellwig 	src_exception.inode = file_inode(src_f);
10604bdf87ebSChristoph Hellwig 	src_exception.state = src_lock->open_context->state;
10614bdf87ebSChristoph Hellwig 
10624bdf87ebSChristoph Hellwig 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f));
10634bdf87ebSChristoph Hellwig 	if (IS_ERR(dst_lock)) {
10644bdf87ebSChristoph Hellwig 		err = PTR_ERR(dst_lock);
10654bdf87ebSChristoph Hellwig 		goto out_put_src_lock;
10664bdf87ebSChristoph Hellwig 	}
10674bdf87ebSChristoph Hellwig 
10684bdf87ebSChristoph Hellwig 	dst_exception.inode = file_inode(dst_f);
10694bdf87ebSChristoph Hellwig 	dst_exception.state = dst_lock->open_context->state;
10704bdf87ebSChristoph Hellwig 
1071e5341f3aSPeng Tao 	do {
10724bdf87ebSChristoph Hellwig 		err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock,
10734bdf87ebSChristoph Hellwig 					src_offset, dst_offset, count);
1074e5341f3aSPeng Tao 		if (err == -ENOTSUPP || err == -EOPNOTSUPP) {
1075e5341f3aSPeng Tao 			NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE;
10764bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
10774bdf87ebSChristoph Hellwig 			break;
1078e5341f3aSPeng Tao 		}
1079e5341f3aSPeng Tao 
10804bdf87ebSChristoph Hellwig 		err2 = nfs4_handle_exception(server, err, &src_exception);
10814bdf87ebSChristoph Hellwig 		err = nfs4_handle_exception(server, err, &dst_exception);
10824bdf87ebSChristoph Hellwig 		if (!err)
10834bdf87ebSChristoph Hellwig 			err = err2;
10844bdf87ebSChristoph Hellwig 	} while (src_exception.retry || dst_exception.retry);
10854bdf87ebSChristoph Hellwig 
10864bdf87ebSChristoph Hellwig 	nfs_put_lock_context(dst_lock);
10874bdf87ebSChristoph Hellwig out_put_src_lock:
10884bdf87ebSChristoph Hellwig 	nfs_put_lock_context(src_lock);
1089e5341f3aSPeng Tao 	return err;
1090e5341f3aSPeng Tao }
1091