xref: /openbmc/linux/fs/nfs/nfs42proc.c (revision 3eb86093)
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>
61c6dcbe5SAnna Schumaker #include <linux/sunrpc/sched.h>
71c6dcbe5SAnna Schumaker #include <linux/nfs.h>
81c6dcbe5SAnna Schumaker #include <linux/nfs3.h>
91c6dcbe5SAnna Schumaker #include <linux/nfs4.h>
101c6dcbe5SAnna Schumaker #include <linux/nfs_xdr.h>
111c6dcbe5SAnna Schumaker #include <linux/nfs_fs.h>
121c6dcbe5SAnna Schumaker #include "nfs4_fs.h"
131c6dcbe5SAnna Schumaker #include "nfs42.h"
141b4a4bd8SPeng Tao #include "iostat.h"
151b4a4bd8SPeng Tao #include "pnfs.h"
16efc6f4aaSAnna Schumaker #include "nfs4session.h"
171b4a4bd8SPeng Tao #include "internal.h"
181b4a4bd8SPeng Tao 
19291e1b94SAnna Schumaker #define NFSDBG_FACILITY NFSDBG_PROC
20c975c209SOlga Kornievskaia static int nfs42_do_offload_cancel_async(struct file *dst, nfs4_stateid *std);
211c6dcbe5SAnna Schumaker 
22f4ac1674SAnna Schumaker static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
234bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, loff_t len)
24f4ac1674SAnna Schumaker {
25f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
269a51940bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
27f4ac1674SAnna Schumaker 	struct nfs42_falloc_args args = {
28f4ac1674SAnna Schumaker 		.falloc_fh	= NFS_FH(inode),
29f4ac1674SAnna Schumaker 		.falloc_offset	= offset,
30f4ac1674SAnna Schumaker 		.falloc_length	= len,
319a51940bSAnna Schumaker 		.falloc_bitmask	= server->cache_consistency_bitmask,
32f4ac1674SAnna Schumaker 	};
339a51940bSAnna Schumaker 	struct nfs42_falloc_res res = {
349a51940bSAnna Schumaker 		.falloc_server	= server,
359a51940bSAnna Schumaker 	};
36f4ac1674SAnna Schumaker 	int status;
37f4ac1674SAnna Schumaker 
38f4ac1674SAnna Schumaker 	msg->rpc_argp = &args;
39f4ac1674SAnna Schumaker 	msg->rpc_resp = &res;
40f4ac1674SAnna Schumaker 
414bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.falloc_stateid, lock->open_context,
424bdf87ebSChristoph Hellwig 			lock, FMODE_WRITE);
43f4ac1674SAnna Schumaker 	if (status)
44f4ac1674SAnna Schumaker 		return status;
45f4ac1674SAnna Schumaker 
469a51940bSAnna Schumaker 	res.falloc_fattr = nfs_alloc_fattr();
479a51940bSAnna Schumaker 	if (!res.falloc_fattr)
489a51940bSAnna Schumaker 		return -ENOMEM;
499a51940bSAnna Schumaker 
509a51940bSAnna Schumaker 	status = nfs4_call_sync(server->client, server, msg,
51f4ac1674SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
529a51940bSAnna Schumaker 	if (status == 0)
539a51940bSAnna Schumaker 		status = nfs_post_op_update_inode(inode, res.falloc_fattr);
549a51940bSAnna Schumaker 
559a51940bSAnna Schumaker 	kfree(res.falloc_fattr);
569a51940bSAnna Schumaker 	return status;
57f4ac1674SAnna Schumaker }
58f4ac1674SAnna Schumaker 
59f4ac1674SAnna Schumaker static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
60f4ac1674SAnna Schumaker 				loff_t offset, loff_t len)
61f4ac1674SAnna Schumaker {
62f4ac1674SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
63f4ac1674SAnna Schumaker 	struct nfs4_exception exception = { };
644bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
65f4ac1674SAnna Schumaker 	int err;
66f4ac1674SAnna Schumaker 
674bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
684bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
694bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
704bdf87ebSChristoph Hellwig 
714bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
724bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
734bdf87ebSChristoph Hellwig 
74f4ac1674SAnna Schumaker 	do {
754bdf87ebSChristoph Hellwig 		err = _nfs42_proc_fallocate(msg, filep, lock, offset, len);
764bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
774bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
784bdf87ebSChristoph Hellwig 			break;
794bdf87ebSChristoph Hellwig 		}
80f4ac1674SAnna Schumaker 		err = nfs4_handle_exception(server, err, &exception);
81f4ac1674SAnna Schumaker 	} while (exception.retry);
82f4ac1674SAnna Schumaker 
834bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
84f4ac1674SAnna Schumaker 	return err;
85f4ac1674SAnna Schumaker }
86f4ac1674SAnna Schumaker 
87f4ac1674SAnna Schumaker int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
88f4ac1674SAnna Schumaker {
89f4ac1674SAnna Schumaker 	struct rpc_message msg = {
90f4ac1674SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE],
91f4ac1674SAnna Schumaker 	};
92f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
93f4ac1674SAnna Schumaker 	int err;
94f4ac1674SAnna Schumaker 
95f4ac1674SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE))
96f4ac1674SAnna Schumaker 		return -EOPNOTSUPP;
97f4ac1674SAnna Schumaker 
985955102cSAl Viro 	inode_lock(inode);
99f830f7ddSAnna Schumaker 
100f4ac1674SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
101f4ac1674SAnna Schumaker 	if (err == -EOPNOTSUPP)
102f4ac1674SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE;
103f830f7ddSAnna Schumaker 
1045955102cSAl Viro 	inode_unlock(inode);
105f4ac1674SAnna Schumaker 	return err;
106f4ac1674SAnna Schumaker }
107f4ac1674SAnna Schumaker 
108624bd5b7SAnna Schumaker int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
109624bd5b7SAnna Schumaker {
110624bd5b7SAnna Schumaker 	struct rpc_message msg = {
111624bd5b7SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE],
112624bd5b7SAnna Schumaker 	};
113624bd5b7SAnna Schumaker 	struct inode *inode = file_inode(filep);
114624bd5b7SAnna Schumaker 	int err;
115624bd5b7SAnna Schumaker 
116624bd5b7SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE))
117624bd5b7SAnna Schumaker 		return -EOPNOTSUPP;
118624bd5b7SAnna Schumaker 
1195955102cSAl Viro 	inode_lock(inode);
1201e564d3dSTrond Myklebust 	err = nfs_sync_inode(inode);
1211e564d3dSTrond Myklebust 	if (err)
1221e564d3dSTrond Myklebust 		goto out_unlock;
123f830f7ddSAnna Schumaker 
124624bd5b7SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
1259a51940bSAnna Schumaker 	if (err == 0)
1269a51940bSAnna Schumaker 		truncate_pagecache_range(inode, offset, (offset + len) -1);
127624bd5b7SAnna Schumaker 	if (err == -EOPNOTSUPP)
128624bd5b7SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE;
1291e564d3dSTrond Myklebust out_unlock:
1305955102cSAl Viro 	inode_unlock(inode);
131624bd5b7SAnna Schumaker 	return err;
132624bd5b7SAnna Schumaker }
133624bd5b7SAnna Schumaker 
13462164f31SOlga Kornievskaia static int handle_async_copy(struct nfs42_copy_res *res,
13562164f31SOlga Kornievskaia 			     struct nfs_server *server,
13662164f31SOlga Kornievskaia 			     struct file *src,
13762164f31SOlga Kornievskaia 			     struct file *dst,
13862164f31SOlga Kornievskaia 			     nfs4_stateid *src_stateid)
13962164f31SOlga Kornievskaia {
14099f2c555SOlga Kornievskaia 	struct nfs4_copy_state *copy, *tmp_copy;
14162164f31SOlga Kornievskaia 	int status = NFS4_OK;
142bc0c9079SOlga Kornievskaia 	bool found_pending = false;
143e4648aa4SOlga Kornievskaia 	struct nfs_open_context *ctx = nfs_file_open_context(dst);
144bc0c9079SOlga Kornievskaia 
14599f2c555SOlga Kornievskaia 	copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
14699f2c555SOlga Kornievskaia 	if (!copy)
14799f2c555SOlga Kornievskaia 		return -ENOMEM;
14899f2c555SOlga Kornievskaia 
149bc0c9079SOlga Kornievskaia 	spin_lock(&server->nfs_client->cl_lock);
15099f2c555SOlga Kornievskaia 	list_for_each_entry(tmp_copy, &server->nfs_client->pending_cb_stateids,
151bc0c9079SOlga Kornievskaia 				copies) {
15299f2c555SOlga Kornievskaia 		if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
153bc0c9079SOlga Kornievskaia 				NFS4_STATEID_SIZE))
154bc0c9079SOlga Kornievskaia 			continue;
155bc0c9079SOlga Kornievskaia 		found_pending = true;
15699f2c555SOlga Kornievskaia 		list_del(&tmp_copy->copies);
157bc0c9079SOlga Kornievskaia 		break;
158bc0c9079SOlga Kornievskaia 	}
159bc0c9079SOlga Kornievskaia 	if (found_pending) {
160bc0c9079SOlga Kornievskaia 		spin_unlock(&server->nfs_client->cl_lock);
16199f2c555SOlga Kornievskaia 		kfree(copy);
16299f2c555SOlga Kornievskaia 		copy = tmp_copy;
163bc0c9079SOlga Kornievskaia 		goto out;
164bc0c9079SOlga Kornievskaia 	}
16562164f31SOlga Kornievskaia 
16662164f31SOlga Kornievskaia 	memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
16762164f31SOlga Kornievskaia 	init_completion(&copy->completion);
168e4648aa4SOlga Kornievskaia 	copy->parent_state = ctx->state;
16962164f31SOlga Kornievskaia 
17062164f31SOlga Kornievskaia 	list_add_tail(&copy->copies, &server->ss_copies);
17162164f31SOlga Kornievskaia 	spin_unlock(&server->nfs_client->cl_lock);
17262164f31SOlga Kornievskaia 
173c975c209SOlga Kornievskaia 	status = wait_for_completion_interruptible(&copy->completion);
17462164f31SOlga Kornievskaia 	spin_lock(&server->nfs_client->cl_lock);
17562164f31SOlga Kornievskaia 	list_del_init(&copy->copies);
17662164f31SOlga Kornievskaia 	spin_unlock(&server->nfs_client->cl_lock);
177c975c209SOlga Kornievskaia 	if (status == -ERESTARTSYS) {
178e4648aa4SOlga Kornievskaia 		goto out_cancel;
179e4648aa4SOlga Kornievskaia 	} else if (copy->flags) {
180e4648aa4SOlga Kornievskaia 		status = -EAGAIN;
181e4648aa4SOlga Kornievskaia 		goto out_cancel;
182c975c209SOlga Kornievskaia 	}
183bc0c9079SOlga Kornievskaia out:
18462164f31SOlga Kornievskaia 	res->write_res.count = copy->count;
18562164f31SOlga Kornievskaia 	memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
18662164f31SOlga Kornievskaia 	status = -copy->error;
18762164f31SOlga Kornievskaia 
18862164f31SOlga Kornievskaia 	kfree(copy);
18962164f31SOlga Kornievskaia 	return status;
190e4648aa4SOlga Kornievskaia out_cancel:
191e4648aa4SOlga Kornievskaia 	nfs42_do_offload_cancel_async(dst, &copy->stateid);
192e4648aa4SOlga Kornievskaia 	kfree(copy);
193e4648aa4SOlga Kornievskaia 	return status;
19462164f31SOlga Kornievskaia }
19562164f31SOlga Kornievskaia 
1966b8d84e2SOlga Kornievskaia static int process_copy_commit(struct file *dst, loff_t pos_dst,
1976b8d84e2SOlga Kornievskaia 			       struct nfs42_copy_res *res)
1986b8d84e2SOlga Kornievskaia {
1996b8d84e2SOlga Kornievskaia 	struct nfs_commitres cres;
2006b8d84e2SOlga Kornievskaia 	int status = -ENOMEM;
2016b8d84e2SOlga Kornievskaia 
2026b8d84e2SOlga Kornievskaia 	cres.verf = kzalloc(sizeof(struct nfs_writeverf), GFP_NOFS);
2036b8d84e2SOlga Kornievskaia 	if (!cres.verf)
2046b8d84e2SOlga Kornievskaia 		goto out;
2056b8d84e2SOlga Kornievskaia 
2066b8d84e2SOlga Kornievskaia 	status = nfs4_proc_commit(dst, pos_dst, res->write_res.count, &cres);
2076b8d84e2SOlga Kornievskaia 	if (status)
2086b8d84e2SOlga Kornievskaia 		goto out_free;
2096b8d84e2SOlga Kornievskaia 	if (nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
2106b8d84e2SOlga Kornievskaia 				    &cres.verf->verifier)) {
2116b8d84e2SOlga Kornievskaia 		dprintk("commit verf differs from copy verf\n");
2126b8d84e2SOlga Kornievskaia 		status = -EAGAIN;
2136b8d84e2SOlga Kornievskaia 	}
2146b8d84e2SOlga Kornievskaia out_free:
2156b8d84e2SOlga Kornievskaia 	kfree(cres.verf);
2166b8d84e2SOlga Kornievskaia out:
2176b8d84e2SOlga Kornievskaia 	return status;
2186b8d84e2SOlga Kornievskaia }
2196b8d84e2SOlga Kornievskaia 
2209d8cacbfSTrond Myklebust static ssize_t _nfs42_proc_copy(struct file *src,
2212e72448bSAnna Schumaker 				struct nfs_lock_context *src_lock,
2229d8cacbfSTrond Myklebust 				struct file *dst,
2232e72448bSAnna Schumaker 				struct nfs_lock_context *dst_lock,
2249d8cacbfSTrond Myklebust 				struct nfs42_copy_args *args,
2259d8cacbfSTrond Myklebust 				struct nfs42_copy_res *res)
2262e72448bSAnna Schumaker {
2272e72448bSAnna Schumaker 	struct rpc_message msg = {
2282e72448bSAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY],
2299d8cacbfSTrond Myklebust 		.rpc_argp = args,
2309d8cacbfSTrond Myklebust 		.rpc_resp = res,
2312e72448bSAnna Schumaker 	};
2322e72448bSAnna Schumaker 	struct inode *dst_inode = file_inode(dst);
2332e72448bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(dst_inode);
2349d8cacbfSTrond Myklebust 	loff_t pos_src = args->src_pos;
2359d8cacbfSTrond Myklebust 	loff_t pos_dst = args->dst_pos;
2369d8cacbfSTrond Myklebust 	size_t count = args->count;
2371ee48bddSOlga Kornievskaia 	ssize_t status;
2382e72448bSAnna Schumaker 
2399d8cacbfSTrond Myklebust 	status = nfs4_set_rw_stateid(&args->src_stateid, src_lock->open_context,
2402e72448bSAnna Schumaker 				     src_lock, FMODE_READ);
2412e72448bSAnna Schumaker 	if (status)
2422e72448bSAnna Schumaker 		return status;
2432e72448bSAnna Schumaker 
244837bb1d7STrond Myklebust 	status = nfs_filemap_write_and_wait_range(file_inode(src)->i_mapping,
245837bb1d7STrond Myklebust 			pos_src, pos_src + (loff_t)count - 1);
246837bb1d7STrond Myklebust 	if (status)
247837bb1d7STrond Myklebust 		return status;
248837bb1d7STrond Myklebust 
2499d8cacbfSTrond Myklebust 	status = nfs4_set_rw_stateid(&args->dst_stateid, dst_lock->open_context,
2502e72448bSAnna Schumaker 				     dst_lock, FMODE_WRITE);
2512e72448bSAnna Schumaker 	if (status)
2522e72448bSAnna Schumaker 		return status;
2532e72448bSAnna Schumaker 
254837bb1d7STrond Myklebust 	status = nfs_sync_inode(dst_inode);
255837bb1d7STrond Myklebust 	if (status)
256837bb1d7STrond Myklebust 		return status;
257837bb1d7STrond Myklebust 
25862164f31SOlga Kornievskaia 	res->commit_res.verf = NULL;
25962164f31SOlga Kornievskaia 	if (args->sync) {
26062164f31SOlga Kornievskaia 		res->commit_res.verf =
26162164f31SOlga Kornievskaia 			kzalloc(sizeof(struct nfs_writeverf), GFP_NOFS);
262e0926934SOlga Kornievskaia 		if (!res->commit_res.verf)
263e0926934SOlga Kornievskaia 			return -ENOMEM;
26462164f31SOlga Kornievskaia 	}
265e4648aa4SOlga Kornievskaia 	set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
266e4648aa4SOlga Kornievskaia 		&dst_lock->open_context->state->flags);
267e4648aa4SOlga Kornievskaia 
2682e72448bSAnna Schumaker 	status = nfs4_call_sync(server->client, server, &msg,
2699d8cacbfSTrond Myklebust 				&args->seq_args, &res->seq_res, 0);
2702e72448bSAnna Schumaker 	if (status == -ENOTSUPP)
2712e72448bSAnna Schumaker 		server->caps &= ~NFS_CAP_COPY;
2722e72448bSAnna Schumaker 	if (status)
273e0926934SOlga Kornievskaia 		goto out;
2742e72448bSAnna Schumaker 
27562164f31SOlga Kornievskaia 	if (args->sync &&
27662164f31SOlga Kornievskaia 		nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
277e0926934SOlga Kornievskaia 				    &res->commit_res.verf->verifier)) {
278e0926934SOlga Kornievskaia 		status = -EAGAIN;
279e0926934SOlga Kornievskaia 		goto out;
2802e72448bSAnna Schumaker 	}
2812e72448bSAnna Schumaker 
28262164f31SOlga Kornievskaia 	if (!res->synchronous) {
28362164f31SOlga Kornievskaia 		status = handle_async_copy(res, server, src, dst,
28462164f31SOlga Kornievskaia 				&args->src_stateid);
28562164f31SOlga Kornievskaia 		if (status)
28662164f31SOlga Kornievskaia 			return status;
28762164f31SOlga Kornievskaia 	}
28862164f31SOlga Kornievskaia 
2896b8d84e2SOlga Kornievskaia 	if ((!res->synchronous || !args->sync) &&
2906b8d84e2SOlga Kornievskaia 			res->write_res.verifier.committed != NFS_FILE_SYNC) {
2916b8d84e2SOlga Kornievskaia 		status = process_copy_commit(dst, pos_dst, res);
2926b8d84e2SOlga Kornievskaia 		if (status)
2936b8d84e2SOlga Kornievskaia 			return status;
2946b8d84e2SOlga Kornievskaia 	}
2956b8d84e2SOlga Kornievskaia 
2962e72448bSAnna Schumaker 	truncate_pagecache_range(dst_inode, pos_dst,
2979d8cacbfSTrond Myklebust 				 pos_dst + res->write_res.count);
2982e72448bSAnna Schumaker 
299e0926934SOlga Kornievskaia 	status = res->write_res.count;
300e0926934SOlga Kornievskaia out:
30162164f31SOlga Kornievskaia 	if (args->sync)
302e0926934SOlga Kornievskaia 		kfree(res->commit_res.verf);
303e0926934SOlga Kornievskaia 	return status;
3042e72448bSAnna Schumaker }
3052e72448bSAnna Schumaker 
3062e72448bSAnna Schumaker ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
3072e72448bSAnna Schumaker 			struct file *dst, loff_t pos_dst,
3082e72448bSAnna Schumaker 			size_t count)
3092e72448bSAnna Schumaker {
3102e72448bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(dst));
3112e72448bSAnna Schumaker 	struct nfs_lock_context *src_lock;
3122e72448bSAnna Schumaker 	struct nfs_lock_context *dst_lock;
3139d8cacbfSTrond Myklebust 	struct nfs42_copy_args args = {
3149d8cacbfSTrond Myklebust 		.src_fh		= NFS_FH(file_inode(src)),
3159d8cacbfSTrond Myklebust 		.src_pos	= pos_src,
3169d8cacbfSTrond Myklebust 		.dst_fh		= NFS_FH(file_inode(dst)),
3179d8cacbfSTrond Myklebust 		.dst_pos	= pos_dst,
3189d8cacbfSTrond Myklebust 		.count		= count,
31962164f31SOlga Kornievskaia 		.sync		= false,
3209d8cacbfSTrond Myklebust 	};
3219d8cacbfSTrond Myklebust 	struct nfs42_copy_res res;
3229d8cacbfSTrond Myklebust 	struct nfs4_exception src_exception = {
3239d8cacbfSTrond Myklebust 		.inode		= file_inode(src),
3249d8cacbfSTrond Myklebust 		.stateid	= &args.src_stateid,
3259d8cacbfSTrond Myklebust 	};
3269d8cacbfSTrond Myklebust 	struct nfs4_exception dst_exception = {
3279d8cacbfSTrond Myklebust 		.inode		= file_inode(dst),
3289d8cacbfSTrond Myklebust 		.stateid	= &args.dst_stateid,
3299d8cacbfSTrond Myklebust 	};
3302e72448bSAnna Schumaker 	ssize_t err, err2;
3312e72448bSAnna Schumaker 
3322e72448bSAnna Schumaker 	if (!nfs_server_capable(file_inode(dst), NFS_CAP_COPY))
3332e72448bSAnna Schumaker 		return -EOPNOTSUPP;
3342e72448bSAnna Schumaker 
3352e72448bSAnna Schumaker 	src_lock = nfs_get_lock_context(nfs_file_open_context(src));
3362e72448bSAnna Schumaker 	if (IS_ERR(src_lock))
3372e72448bSAnna Schumaker 		return PTR_ERR(src_lock);
3382e72448bSAnna Schumaker 
3392e72448bSAnna Schumaker 	src_exception.state = src_lock->open_context->state;
3402e72448bSAnna Schumaker 
3412e72448bSAnna Schumaker 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst));
3422e72448bSAnna Schumaker 	if (IS_ERR(dst_lock)) {
3432e72448bSAnna Schumaker 		err = PTR_ERR(dst_lock);
3442e72448bSAnna Schumaker 		goto out_put_src_lock;
3452e72448bSAnna Schumaker 	}
3462e72448bSAnna Schumaker 
3472e72448bSAnna Schumaker 	dst_exception.state = dst_lock->open_context->state;
3482e72448bSAnna Schumaker 
3492e72448bSAnna Schumaker 	do {
350ea8ea737SLinus Torvalds 		inode_lock(file_inode(dst));
3519d8cacbfSTrond Myklebust 		err = _nfs42_proc_copy(src, src_lock,
3529d8cacbfSTrond Myklebust 				dst, dst_lock,
3539d8cacbfSTrond Myklebust 				&args, &res);
354ea8ea737SLinus Torvalds 		inode_unlock(file_inode(dst));
3552e72448bSAnna Schumaker 
3569d8cacbfSTrond Myklebust 		if (err >= 0)
3579d8cacbfSTrond Myklebust 			break;
3582e72448bSAnna Schumaker 		if (err == -ENOTSUPP) {
3592e72448bSAnna Schumaker 			err = -EOPNOTSUPP;
3602e72448bSAnna Schumaker 			break;
361539f57b3SOlga Kornievskaia 		} else if (err == -EAGAIN) {
362539f57b3SOlga Kornievskaia 			dst_exception.retry = 1;
363539f57b3SOlga Kornievskaia 			continue;
364539f57b3SOlga Kornievskaia 		} else if (err == -NFS4ERR_OFFLOAD_NO_REQS && !args.sync) {
365539f57b3SOlga Kornievskaia 			args.sync = true;
366e0926934SOlga Kornievskaia 			dst_exception.retry = 1;
367e0926934SOlga Kornievskaia 			continue;
3682e72448bSAnna Schumaker 		}
3692e72448bSAnna Schumaker 
3702e72448bSAnna Schumaker 		err2 = nfs4_handle_exception(server, err, &src_exception);
3712e72448bSAnna Schumaker 		err  = nfs4_handle_exception(server, err, &dst_exception);
3722e72448bSAnna Schumaker 		if (!err)
3732e72448bSAnna Schumaker 			err = err2;
3742e72448bSAnna Schumaker 	} while (src_exception.retry || dst_exception.retry);
3752e72448bSAnna Schumaker 
3762e72448bSAnna Schumaker 	nfs_put_lock_context(dst_lock);
3772e72448bSAnna Schumaker out_put_src_lock:
3782e72448bSAnna Schumaker 	nfs_put_lock_context(src_lock);
3792e72448bSAnna Schumaker 	return err;
3802e72448bSAnna Schumaker }
3812e72448bSAnna Schumaker 
382c975c209SOlga Kornievskaia struct nfs42_offloadcancel_data {
383c975c209SOlga Kornievskaia 	struct nfs_server *seq_server;
384c975c209SOlga Kornievskaia 	struct nfs42_offload_status_args args;
385c975c209SOlga Kornievskaia 	struct nfs42_offload_status_res res;
386c975c209SOlga Kornievskaia };
387c975c209SOlga Kornievskaia 
388c975c209SOlga Kornievskaia static void nfs42_offload_cancel_prepare(struct rpc_task *task, void *calldata)
389c975c209SOlga Kornievskaia {
390c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
391c975c209SOlga Kornievskaia 
392c975c209SOlga Kornievskaia 	nfs4_setup_sequence(data->seq_server->nfs_client,
393c975c209SOlga Kornievskaia 				&data->args.osa_seq_args,
394c975c209SOlga Kornievskaia 				&data->res.osr_seq_res, task);
395c975c209SOlga Kornievskaia }
396c975c209SOlga Kornievskaia 
397c975c209SOlga Kornievskaia static void nfs42_offload_cancel_done(struct rpc_task *task, void *calldata)
398c975c209SOlga Kornievskaia {
399c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
400c975c209SOlga Kornievskaia 
401c975c209SOlga Kornievskaia 	nfs41_sequence_done(task, &data->res.osr_seq_res);
402c975c209SOlga Kornievskaia 	if (task->tk_status &&
403c975c209SOlga Kornievskaia 		nfs4_async_handle_error(task, data->seq_server, NULL,
404c975c209SOlga Kornievskaia 			NULL) == -EAGAIN)
405c975c209SOlga Kornievskaia 		rpc_restart_call_prepare(task);
406c975c209SOlga Kornievskaia }
407c975c209SOlga Kornievskaia 
408c975c209SOlga Kornievskaia static void nfs42_free_offloadcancel_data(void *data)
409c975c209SOlga Kornievskaia {
410c975c209SOlga Kornievskaia 	kfree(data);
411c975c209SOlga Kornievskaia }
412c975c209SOlga Kornievskaia 
413c975c209SOlga Kornievskaia static const struct rpc_call_ops nfs42_offload_cancel_ops = {
414c975c209SOlga Kornievskaia 	.rpc_call_prepare = nfs42_offload_cancel_prepare,
415c975c209SOlga Kornievskaia 	.rpc_call_done = nfs42_offload_cancel_done,
416c975c209SOlga Kornievskaia 	.rpc_release = nfs42_free_offloadcancel_data,
417c975c209SOlga Kornievskaia };
418c975c209SOlga Kornievskaia 
419c975c209SOlga Kornievskaia static int nfs42_do_offload_cancel_async(struct file *dst,
420c975c209SOlga Kornievskaia 					 nfs4_stateid *stateid)
421c975c209SOlga Kornievskaia {
422c975c209SOlga Kornievskaia 	struct nfs_server *dst_server = NFS_SERVER(file_inode(dst));
423c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = NULL;
424c975c209SOlga Kornievskaia 	struct nfs_open_context *ctx = nfs_file_open_context(dst);
425c975c209SOlga Kornievskaia 	struct rpc_task *task;
426c975c209SOlga Kornievskaia 	struct rpc_message msg = {
427c975c209SOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OFFLOAD_CANCEL],
428c975c209SOlga Kornievskaia 		.rpc_cred = ctx->cred,
429c975c209SOlga Kornievskaia 	};
430c975c209SOlga Kornievskaia 	struct rpc_task_setup task_setup_data = {
431c975c209SOlga Kornievskaia 		.rpc_client = dst_server->client,
432c975c209SOlga Kornievskaia 		.rpc_message = &msg,
433c975c209SOlga Kornievskaia 		.callback_ops = &nfs42_offload_cancel_ops,
434c975c209SOlga Kornievskaia 		.workqueue = nfsiod_workqueue,
435c975c209SOlga Kornievskaia 		.flags = RPC_TASK_ASYNC,
436c975c209SOlga Kornievskaia 	};
437c975c209SOlga Kornievskaia 	int status;
438c975c209SOlga Kornievskaia 
439c975c209SOlga Kornievskaia 	if (!(dst_server->caps & NFS_CAP_OFFLOAD_CANCEL))
440c975c209SOlga Kornievskaia 		return -EOPNOTSUPP;
441c975c209SOlga Kornievskaia 
442c975c209SOlga Kornievskaia 	data = kzalloc(sizeof(struct nfs42_offloadcancel_data), GFP_NOFS);
443c975c209SOlga Kornievskaia 	if (data == NULL)
444c975c209SOlga Kornievskaia 		return -ENOMEM;
445c975c209SOlga Kornievskaia 
446c975c209SOlga Kornievskaia 	data->seq_server = dst_server;
447c975c209SOlga Kornievskaia 	data->args.osa_src_fh = NFS_FH(file_inode(dst));
448c975c209SOlga Kornievskaia 	memcpy(&data->args.osa_stateid, stateid,
449c975c209SOlga Kornievskaia 		sizeof(data->args.osa_stateid));
450c975c209SOlga Kornievskaia 	msg.rpc_argp = &data->args;
451c975c209SOlga Kornievskaia 	msg.rpc_resp = &data->res;
452c975c209SOlga Kornievskaia 	task_setup_data.callback_data = data;
453c975c209SOlga Kornievskaia 	nfs4_init_sequence(&data->args.osa_seq_args, &data->res.osr_seq_res,
454c975c209SOlga Kornievskaia 			   1, 0);
455c975c209SOlga Kornievskaia 	task = rpc_run_task(&task_setup_data);
456c975c209SOlga Kornievskaia 	if (IS_ERR(task))
457c975c209SOlga Kornievskaia 		return PTR_ERR(task);
458c975c209SOlga Kornievskaia 	status = rpc_wait_for_completion_task(task);
459c975c209SOlga Kornievskaia 	if (status == -ENOTSUPP)
460c975c209SOlga Kornievskaia 		dst_server->caps &= ~NFS_CAP_OFFLOAD_CANCEL;
461c975c209SOlga Kornievskaia 	rpc_put_task(task);
462c975c209SOlga Kornievskaia 	return status;
463c975c209SOlga Kornievskaia }
464c975c209SOlga Kornievskaia 
4654bdf87ebSChristoph Hellwig static loff_t _nfs42_proc_llseek(struct file *filep,
4664bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, int whence)
4671c6dcbe5SAnna Schumaker {
4681c6dcbe5SAnna Schumaker 	struct inode *inode = file_inode(filep);
4691c6dcbe5SAnna Schumaker 	struct nfs42_seek_args args = {
4701c6dcbe5SAnna Schumaker 		.sa_fh		= NFS_FH(inode),
4711c6dcbe5SAnna Schumaker 		.sa_offset	= offset,
4721c6dcbe5SAnna Schumaker 		.sa_what	= (whence == SEEK_HOLE) ?
4731c6dcbe5SAnna Schumaker 					NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA,
4741c6dcbe5SAnna Schumaker 	};
4751c6dcbe5SAnna Schumaker 	struct nfs42_seek_res res;
4761c6dcbe5SAnna Schumaker 	struct rpc_message msg = {
4771c6dcbe5SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK],
4781c6dcbe5SAnna Schumaker 		.rpc_argp = &args,
4791c6dcbe5SAnna Schumaker 		.rpc_resp = &res,
4801c6dcbe5SAnna Schumaker 	};
4811c6dcbe5SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
4821c6dcbe5SAnna Schumaker 	int status;
4831c6dcbe5SAnna Schumaker 
484878ffa9fSAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_SEEK))
4851c6dcbe5SAnna Schumaker 		return -ENOTSUPP;
4861c6dcbe5SAnna Schumaker 
4874bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context,
4884bdf87ebSChristoph Hellwig 			lock, FMODE_READ);
4891c6dcbe5SAnna Schumaker 	if (status)
4901c6dcbe5SAnna Schumaker 		return status;
4911c6dcbe5SAnna Schumaker 
492e95fc4a0STrond Myklebust 	status = nfs_filemap_write_and_wait_range(inode->i_mapping,
493e95fc4a0STrond Myklebust 			offset, LLONG_MAX);
494e95fc4a0STrond Myklebust 	if (status)
495e95fc4a0STrond Myklebust 		return status;
496e95fc4a0STrond Myklebust 
4971c6dcbe5SAnna Schumaker 	status = nfs4_call_sync(server->client, server, &msg,
4981c6dcbe5SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
4991c6dcbe5SAnna Schumaker 	if (status == -ENOTSUPP)
5001c6dcbe5SAnna Schumaker 		server->caps &= ~NFS_CAP_SEEK;
5011c6dcbe5SAnna Schumaker 	if (status)
5021c6dcbe5SAnna Schumaker 		return status;
5031c6dcbe5SAnna Schumaker 
5041c6dcbe5SAnna Schumaker 	return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes);
5051c6dcbe5SAnna Schumaker }
506be3a5d23STrond Myklebust 
507bdcc2cd1SJ. Bruce Fields loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
508bdcc2cd1SJ. Bruce Fields {
509bdcc2cd1SJ. Bruce Fields 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
510bdcc2cd1SJ. Bruce Fields 	struct nfs4_exception exception = { };
5114bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
512306a5549SJ. Bruce Fields 	loff_t err;
513bdcc2cd1SJ. Bruce Fields 
5144bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
5154bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
5164bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
5174bdf87ebSChristoph Hellwig 
5184bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
5194bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
5204bdf87ebSChristoph Hellwig 
521bdcc2cd1SJ. Bruce Fields 	do {
5224bdf87ebSChristoph Hellwig 		err = _nfs42_proc_llseek(filep, lock, offset, whence);
523306a5549SJ. Bruce Fields 		if (err >= 0)
524306a5549SJ. Bruce Fields 			break;
5254bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
5264bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
5274bdf87ebSChristoph Hellwig 			break;
5284bdf87ebSChristoph Hellwig 		}
529bdcc2cd1SJ. Bruce Fields 		err = nfs4_handle_exception(server, err, &exception);
530bdcc2cd1SJ. Bruce Fields 	} while (exception.retry);
531bdcc2cd1SJ. Bruce Fields 
5324bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
533bdcc2cd1SJ. Bruce Fields 	return err;
534bdcc2cd1SJ. Bruce Fields }
535bdcc2cd1SJ. Bruce Fields 
536bdcc2cd1SJ. Bruce Fields 
5371b4a4bd8SPeng Tao static void
5381b4a4bd8SPeng Tao nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata)
5391b4a4bd8SPeng Tao {
5401b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
5419a0fe867STrond Myklebust 	struct inode *inode = data->inode;
5429a0fe867STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
5439a0fe867STrond Myklebust 	struct pnfs_layout_hdr *lo;
5441b4a4bd8SPeng Tao 
5459a0fe867STrond Myklebust 	spin_lock(&inode->i_lock);
5469a0fe867STrond Myklebust 	lo = NFS_I(inode)->layout;
5479a0fe867STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
5489a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
5499a0fe867STrond Myklebust 		rpc_exit(task, 0);
5509a0fe867STrond Myklebust 		return;
5519a0fe867STrond Myklebust 	}
5529a0fe867STrond Myklebust 	nfs4_stateid_copy(&data->args.stateid, &lo->plh_stateid);
5539a0fe867STrond Myklebust 	spin_unlock(&inode->i_lock);
5546de7e12fSAnna Schumaker 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
5556de7e12fSAnna Schumaker 			    &data->res.seq_res, task);
5561b4a4bd8SPeng Tao }
5571b4a4bd8SPeng Tao 
5581b4a4bd8SPeng Tao static void
5591b4a4bd8SPeng Tao nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
5601b4a4bd8SPeng Tao {
5611b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
56268d264cfSPeng Tao 	struct inode *inode = data->inode;
56368d264cfSPeng Tao 	struct pnfs_layout_hdr *lo;
5641b4a4bd8SPeng Tao 
5651b4a4bd8SPeng Tao 	if (!nfs4_sequence_done(task, &data->res.seq_res))
5661b4a4bd8SPeng Tao 		return;
5671b4a4bd8SPeng Tao 
5686c5a0d89STrond Myklebust 	switch (task->tk_status) {
5696c5a0d89STrond Myklebust 	case 0:
5706c5a0d89STrond Myklebust 		break;
571cf61eb26STrond Myklebust 	case -NFS4ERR_BADHANDLE:
572cf61eb26STrond Myklebust 	case -ESTALE:
573cf61eb26STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
574cf61eb26STrond Myklebust 		break;
57568d264cfSPeng Tao 	case -NFS4ERR_EXPIRED:
576206b3bb5STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
577206b3bb5STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
57868d264cfSPeng Tao 	case -NFS4ERR_STALE_STATEID:
57968d264cfSPeng Tao 	case -NFS4ERR_BAD_STATEID:
58068d264cfSPeng Tao 		spin_lock(&inode->i_lock);
58168d264cfSPeng Tao 		lo = NFS_I(inode)->layout;
5829a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
5839a0fe867STrond Myklebust 		    nfs4_stateid_match(&data->args.stateid,
58468d264cfSPeng Tao 					     &lo->plh_stateid)) {
58568d264cfSPeng Tao 			LIST_HEAD(head);
58668d264cfSPeng Tao 
58768d264cfSPeng Tao 			/*
58868d264cfSPeng Tao 			 * Mark the bad layout state as invalid, then retry
58968d264cfSPeng Tao 			 * with the current stateid.
59068d264cfSPeng Tao 			 */
5915f46be04STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
59268d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
59368d264cfSPeng Tao 			pnfs_free_lseg_list(&head);
5941f18b82cSTrond Myklebust 			nfs_commit_inode(inode, 0);
59568d264cfSPeng Tao 		} else
59668d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
59768d264cfSPeng Tao 		break;
5989a0fe867STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
5999a0fe867STrond Myklebust 		spin_lock(&inode->i_lock);
6009a0fe867STrond Myklebust 		lo = NFS_I(inode)->layout;
6019a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
6029a0fe867STrond Myklebust 		    nfs4_stateid_match_other(&data->args.stateid,
6039a0fe867STrond Myklebust 					&lo->plh_stateid)) {
6049a0fe867STrond Myklebust 			/* Do we need to delay before resending? */
6059a0fe867STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
6069a0fe867STrond Myklebust 						&data->args.stateid))
6079a0fe867STrond Myklebust 				rpc_delay(task, HZ);
6089a0fe867STrond Myklebust 			rpc_restart_call_prepare(task);
6099a0fe867STrond Myklebust 		}
6109a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
6119a0fe867STrond Myklebust 		break;
6126c5a0d89STrond Myklebust 	case -ENOTSUPP:
6136c5a0d89STrond Myklebust 	case -EOPNOTSUPP:
61468d264cfSPeng Tao 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
6151b4a4bd8SPeng Tao 	}
6166c5a0d89STrond Myklebust }
6171b4a4bd8SPeng Tao 
6181b4a4bd8SPeng Tao static void
6191b4a4bd8SPeng Tao nfs42_layoutstat_release(void *calldata)
6201b4a4bd8SPeng Tao {
6211b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
622422c93c8STrond Myklebust 	struct nfs42_layoutstat_devinfo *devinfo = data->args.devinfo;
623422c93c8STrond Myklebust 	int i;
6248733408dSPeng Tao 
625422c93c8STrond Myklebust 	for (i = 0; i < data->args.num_dev; i++) {
626422c93c8STrond Myklebust 		if (devinfo[i].ld_private.ops && devinfo[i].ld_private.ops->free)
627422c93c8STrond Myklebust 			devinfo[i].ld_private.ops->free(&devinfo[i].ld_private);
628422c93c8STrond Myklebust 	}
6291b4a4bd8SPeng Tao 
6301b4a4bd8SPeng Tao 	pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout);
6311bfe3b25SPeng Tao 	smp_mb__before_atomic();
6321bfe3b25SPeng Tao 	clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags);
6331bfe3b25SPeng Tao 	smp_mb__after_atomic();
6341b4a4bd8SPeng Tao 	nfs_iput_and_deactive(data->inode);
6351b4a4bd8SPeng Tao 	kfree(data->args.devinfo);
6361b4a4bd8SPeng Tao 	kfree(data);
6371b4a4bd8SPeng Tao }
6381b4a4bd8SPeng Tao 
639be3a5d23STrond Myklebust static const struct rpc_call_ops nfs42_layoutstat_ops = {
6401b4a4bd8SPeng Tao 	.rpc_call_prepare = nfs42_layoutstat_prepare,
6411b4a4bd8SPeng Tao 	.rpc_call_done = nfs42_layoutstat_done,
6421b4a4bd8SPeng Tao 	.rpc_release = nfs42_layoutstat_release,
643be3a5d23STrond Myklebust };
644be3a5d23STrond Myklebust 
645be3a5d23STrond Myklebust int nfs42_proc_layoutstats_generic(struct nfs_server *server,
646be3a5d23STrond Myklebust 				   struct nfs42_layoutstat_data *data)
647be3a5d23STrond Myklebust {
648be3a5d23STrond Myklebust 	struct rpc_message msg = {
649be3a5d23STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS],
650be3a5d23STrond Myklebust 		.rpc_argp = &data->args,
651be3a5d23STrond Myklebust 		.rpc_resp = &data->res,
652be3a5d23STrond Myklebust 	};
653be3a5d23STrond Myklebust 	struct rpc_task_setup task_setup = {
654be3a5d23STrond Myklebust 		.rpc_client = server->client,
655be3a5d23STrond Myklebust 		.rpc_message = &msg,
656be3a5d23STrond Myklebust 		.callback_ops = &nfs42_layoutstat_ops,
657be3a5d23STrond Myklebust 		.callback_data = data,
658be3a5d23STrond Myklebust 		.flags = RPC_TASK_ASYNC,
659be3a5d23STrond Myklebust 	};
660be3a5d23STrond Myklebust 	struct rpc_task *task;
661be3a5d23STrond Myklebust 
6621b4a4bd8SPeng Tao 	data->inode = nfs_igrab_and_active(data->args.inode);
6631b4a4bd8SPeng Tao 	if (!data->inode) {
6641b4a4bd8SPeng Tao 		nfs42_layoutstat_release(data);
6651b4a4bd8SPeng Tao 		return -EAGAIN;
6661b4a4bd8SPeng Tao 	}
667fba83f34SAnna Schumaker 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
668be3a5d23STrond Myklebust 	task = rpc_run_task(&task_setup);
669be3a5d23STrond Myklebust 	if (IS_ERR(task))
670be3a5d23STrond Myklebust 		return PTR_ERR(task);
6713f807e5aSJeff Layton 	rpc_put_task(task);
672be3a5d23STrond Myklebust 	return 0;
673be3a5d23STrond Myklebust }
674e5341f3aSPeng Tao 
6753eb86093STrond Myklebust static struct nfs42_layouterror_data *
6763eb86093STrond Myklebust nfs42_alloc_layouterror_data(struct pnfs_layout_segment *lseg, gfp_t gfp_flags)
6773eb86093STrond Myklebust {
6783eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
6793eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
6803eb86093STrond Myklebust 
6813eb86093STrond Myklebust 	data = kzalloc(sizeof(*data), gfp_flags);
6823eb86093STrond Myklebust 	if (data) {
6833eb86093STrond Myklebust 		data->args.inode = data->inode = nfs_igrab_and_active(inode);
6843eb86093STrond Myklebust 		if (data->inode) {
6853eb86093STrond Myklebust 			data->lseg = pnfs_get_lseg(lseg);
6863eb86093STrond Myklebust 			if (data->lseg)
6873eb86093STrond Myklebust 				return data;
6883eb86093STrond Myklebust 			nfs_iput_and_deactive(data->inode);
6893eb86093STrond Myklebust 		}
6903eb86093STrond Myklebust 		kfree(data);
6913eb86093STrond Myklebust 	}
6923eb86093STrond Myklebust 	return NULL;
6933eb86093STrond Myklebust }
6943eb86093STrond Myklebust 
6953eb86093STrond Myklebust static void
6963eb86093STrond Myklebust nfs42_free_layouterror_data(struct nfs42_layouterror_data *data)
6973eb86093STrond Myklebust {
6983eb86093STrond Myklebust 	pnfs_put_lseg(data->lseg);
6993eb86093STrond Myklebust 	nfs_iput_and_deactive(data->inode);
7003eb86093STrond Myklebust 	kfree(data);
7013eb86093STrond Myklebust }
7023eb86093STrond Myklebust 
7033eb86093STrond Myklebust static void
7043eb86093STrond Myklebust nfs42_layouterror_prepare(struct rpc_task *task, void *calldata)
7053eb86093STrond Myklebust {
7063eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
7073eb86093STrond Myklebust 	struct inode *inode = data->inode;
7083eb86093STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
7093eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
7103eb86093STrond Myklebust 	unsigned i;
7113eb86093STrond Myklebust 
7123eb86093STrond Myklebust 	spin_lock(&inode->i_lock);
7133eb86093STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
7143eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
7153eb86093STrond Myklebust 		rpc_exit(task, 0);
7163eb86093STrond Myklebust 		return;
7173eb86093STrond Myklebust 	}
7183eb86093STrond Myklebust 	for (i = 0; i < data->args.num_errors; i++)
7193eb86093STrond Myklebust 		nfs4_stateid_copy(&data->args.errors[i].stateid,
7203eb86093STrond Myklebust 				&lo->plh_stateid);
7213eb86093STrond Myklebust 	spin_unlock(&inode->i_lock);
7223eb86093STrond Myklebust 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
7233eb86093STrond Myklebust 			    &data->res.seq_res, task);
7243eb86093STrond Myklebust }
7253eb86093STrond Myklebust 
7263eb86093STrond Myklebust static void
7273eb86093STrond Myklebust nfs42_layouterror_done(struct rpc_task *task, void *calldata)
7283eb86093STrond Myklebust {
7293eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
7303eb86093STrond Myklebust 	struct inode *inode = data->inode;
7313eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
7323eb86093STrond Myklebust 
7333eb86093STrond Myklebust 	if (!nfs4_sequence_done(task, &data->res.seq_res))
7343eb86093STrond Myklebust 		return;
7353eb86093STrond Myklebust 
7363eb86093STrond Myklebust 	switch (task->tk_status) {
7373eb86093STrond Myklebust 	case 0:
7383eb86093STrond Myklebust 		break;
7393eb86093STrond Myklebust 	case -NFS4ERR_BADHANDLE:
7403eb86093STrond Myklebust 	case -ESTALE:
7413eb86093STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
7423eb86093STrond Myklebust 		break;
7433eb86093STrond Myklebust 	case -NFS4ERR_EXPIRED:
7443eb86093STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
7453eb86093STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
7463eb86093STrond Myklebust 	case -NFS4ERR_STALE_STATEID:
7473eb86093STrond Myklebust 	case -NFS4ERR_BAD_STATEID:
7483eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
7493eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
7503eb86093STrond Myklebust 		    nfs4_stateid_match(&data->args.errors[0].stateid,
7513eb86093STrond Myklebust 					     &lo->plh_stateid)) {
7523eb86093STrond Myklebust 			LIST_HEAD(head);
7533eb86093STrond Myklebust 
7543eb86093STrond Myklebust 			/*
7553eb86093STrond Myklebust 			 * Mark the bad layout state as invalid, then retry
7563eb86093STrond Myklebust 			 * with the current stateid.
7573eb86093STrond Myklebust 			 */
7583eb86093STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
7593eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
7603eb86093STrond Myklebust 			pnfs_free_lseg_list(&head);
7613eb86093STrond Myklebust 			nfs_commit_inode(inode, 0);
7623eb86093STrond Myklebust 		} else
7633eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
7643eb86093STrond Myklebust 		break;
7653eb86093STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
7663eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
7673eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
7683eb86093STrond Myklebust 		    nfs4_stateid_match_other(&data->args.errors[0].stateid,
7693eb86093STrond Myklebust 					&lo->plh_stateid)) {
7703eb86093STrond Myklebust 			/* Do we need to delay before resending? */
7713eb86093STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
7723eb86093STrond Myklebust 						&data->args.errors[0].stateid))
7733eb86093STrond Myklebust 				rpc_delay(task, HZ);
7743eb86093STrond Myklebust 			rpc_restart_call_prepare(task);
7753eb86093STrond Myklebust 		}
7763eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
7773eb86093STrond Myklebust 		break;
7783eb86093STrond Myklebust 	case -ENOTSUPP:
7793eb86093STrond Myklebust 	case -EOPNOTSUPP:
7803eb86093STrond Myklebust 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTERROR;
7813eb86093STrond Myklebust 	}
7823eb86093STrond Myklebust }
7833eb86093STrond Myklebust 
7843eb86093STrond Myklebust static void
7853eb86093STrond Myklebust nfs42_layouterror_release(void *calldata)
7863eb86093STrond Myklebust {
7873eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
7883eb86093STrond Myklebust 
7893eb86093STrond Myklebust 	nfs42_free_layouterror_data(data);
7903eb86093STrond Myklebust }
7913eb86093STrond Myklebust 
7923eb86093STrond Myklebust static const struct rpc_call_ops nfs42_layouterror_ops = {
7933eb86093STrond Myklebust 	.rpc_call_prepare = nfs42_layouterror_prepare,
7943eb86093STrond Myklebust 	.rpc_call_done = nfs42_layouterror_done,
7953eb86093STrond Myklebust 	.rpc_release = nfs42_layouterror_release,
7963eb86093STrond Myklebust };
7973eb86093STrond Myklebust 
7983eb86093STrond Myklebust int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
7993eb86093STrond Myklebust 		const struct nfs42_layout_error *errors, size_t n)
8003eb86093STrond Myklebust {
8013eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
8023eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
8033eb86093STrond Myklebust 	struct rpc_task *task;
8043eb86093STrond Myklebust 	struct rpc_message msg = {
8053eb86093STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTERROR],
8063eb86093STrond Myklebust 	};
8073eb86093STrond Myklebust 	struct rpc_task_setup task_setup = {
8083eb86093STrond Myklebust 		.rpc_message = &msg,
8093eb86093STrond Myklebust 		.callback_ops = &nfs42_layouterror_ops,
8103eb86093STrond Myklebust 		.flags = RPC_TASK_ASYNC,
8113eb86093STrond Myklebust 	};
8123eb86093STrond Myklebust 	unsigned int i;
8133eb86093STrond Myklebust 
8143eb86093STrond Myklebust 	if (!nfs_server_capable(inode, NFS_CAP_LAYOUTERROR))
8153eb86093STrond Myklebust 		return -EOPNOTSUPP;
8163eb86093STrond Myklebust 	if (n > NFS42_LAYOUTERROR_MAX)
8173eb86093STrond Myklebust 		return -EINVAL;
8183eb86093STrond Myklebust 	data = nfs42_alloc_layouterror_data(lseg, GFP_NOFS);
8193eb86093STrond Myklebust 	if (!data)
8203eb86093STrond Myklebust 		return -ENOMEM;
8213eb86093STrond Myklebust 	for (i = 0; i < n; i++) {
8223eb86093STrond Myklebust 		data->args.errors[i] = errors[i];
8233eb86093STrond Myklebust 		data->args.num_errors++;
8243eb86093STrond Myklebust 		data->res.num_errors++;
8253eb86093STrond Myklebust 	}
8263eb86093STrond Myklebust 	msg.rpc_argp = &data->args;
8273eb86093STrond Myklebust 	msg.rpc_resp = &data->res;
8283eb86093STrond Myklebust 	task_setup.callback_data = data;
8293eb86093STrond Myklebust 	task_setup.rpc_client = NFS_SERVER(inode)->client;
8303eb86093STrond Myklebust 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
8313eb86093STrond Myklebust 	task = rpc_run_task(&task_setup);
8323eb86093STrond Myklebust 	if (IS_ERR(task))
8333eb86093STrond Myklebust 		return PTR_ERR(task);
8343eb86093STrond Myklebust 	rpc_put_task(task);
8353eb86093STrond Myklebust 	return 0;
8363eb86093STrond Myklebust }
8373eb86093STrond Myklebust EXPORT_SYMBOL_GPL(nfs42_proc_layouterror);
8383eb86093STrond Myklebust 
839e5341f3aSPeng Tao static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
8404bdf87ebSChristoph Hellwig 		struct file *dst_f, struct nfs_lock_context *src_lock,
8414bdf87ebSChristoph Hellwig 		struct nfs_lock_context *dst_lock, loff_t src_offset,
842e5341f3aSPeng Tao 		loff_t dst_offset, loff_t count)
843e5341f3aSPeng Tao {
844e5341f3aSPeng Tao 	struct inode *src_inode = file_inode(src_f);
845e5341f3aSPeng Tao 	struct inode *dst_inode = file_inode(dst_f);
846e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(dst_inode);
847e5341f3aSPeng Tao 	struct nfs42_clone_args args = {
848e5341f3aSPeng Tao 		.src_fh = NFS_FH(src_inode),
849e5341f3aSPeng Tao 		.dst_fh = NFS_FH(dst_inode),
850e5341f3aSPeng Tao 		.src_offset = src_offset,
851e5341f3aSPeng Tao 		.dst_offset = dst_offset,
8529494b2ceSChristoph Hellwig 		.count = count,
853e5341f3aSPeng Tao 		.dst_bitmask = server->cache_consistency_bitmask,
854e5341f3aSPeng Tao 	};
855e5341f3aSPeng Tao 	struct nfs42_clone_res res = {
856e5341f3aSPeng Tao 		.server	= server,
857e5341f3aSPeng Tao 	};
858e5341f3aSPeng Tao 	int status;
859e5341f3aSPeng Tao 
860e5341f3aSPeng Tao 	msg->rpc_argp = &args;
861e5341f3aSPeng Tao 	msg->rpc_resp = &res;
862e5341f3aSPeng Tao 
8634bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context,
8644bdf87ebSChristoph Hellwig 			src_lock, FMODE_READ);
865e5341f3aSPeng Tao 	if (status)
866e5341f3aSPeng Tao 		return status;
867e5341f3aSPeng Tao 
8684bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
8694bdf87ebSChristoph Hellwig 			dst_lock, FMODE_WRITE);
870e5341f3aSPeng Tao 	if (status)
871e5341f3aSPeng Tao 		return status;
872e5341f3aSPeng Tao 
873e5341f3aSPeng Tao 	res.dst_fattr = nfs_alloc_fattr();
874e5341f3aSPeng Tao 	if (!res.dst_fattr)
875e5341f3aSPeng Tao 		return -ENOMEM;
876e5341f3aSPeng Tao 
877e5341f3aSPeng Tao 	status = nfs4_call_sync(server->client, server, msg,
878e5341f3aSPeng Tao 				&args.seq_args, &res.seq_res, 0);
879e5341f3aSPeng Tao 	if (status == 0)
880e5341f3aSPeng Tao 		status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
881e5341f3aSPeng Tao 
882e5341f3aSPeng Tao 	kfree(res.dst_fattr);
883e5341f3aSPeng Tao 	return status;
884e5341f3aSPeng Tao }
885e5341f3aSPeng Tao 
886e5341f3aSPeng Tao int nfs42_proc_clone(struct file *src_f, struct file *dst_f,
887e5341f3aSPeng Tao 		     loff_t src_offset, loff_t dst_offset, loff_t count)
888e5341f3aSPeng Tao {
889e5341f3aSPeng Tao 	struct rpc_message msg = {
890e5341f3aSPeng Tao 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE],
891e5341f3aSPeng Tao 	};
892e5341f3aSPeng Tao 	struct inode *inode = file_inode(src_f);
893e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(file_inode(src_f));
8944bdf87ebSChristoph Hellwig 	struct nfs_lock_context *src_lock;
8954bdf87ebSChristoph Hellwig 	struct nfs_lock_context *dst_lock;
8964bdf87ebSChristoph Hellwig 	struct nfs4_exception src_exception = { };
8974bdf87ebSChristoph Hellwig 	struct nfs4_exception dst_exception = { };
8984bdf87ebSChristoph Hellwig 	int err, err2;
899e5341f3aSPeng Tao 
900e5341f3aSPeng Tao 	if (!nfs_server_capable(inode, NFS_CAP_CLONE))
901e5341f3aSPeng Tao 		return -EOPNOTSUPP;
902e5341f3aSPeng Tao 
9034bdf87ebSChristoph Hellwig 	src_lock = nfs_get_lock_context(nfs_file_open_context(src_f));
9044bdf87ebSChristoph Hellwig 	if (IS_ERR(src_lock))
9054bdf87ebSChristoph Hellwig 		return PTR_ERR(src_lock);
9064bdf87ebSChristoph Hellwig 
9074bdf87ebSChristoph Hellwig 	src_exception.inode = file_inode(src_f);
9084bdf87ebSChristoph Hellwig 	src_exception.state = src_lock->open_context->state;
9094bdf87ebSChristoph Hellwig 
9104bdf87ebSChristoph Hellwig 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f));
9114bdf87ebSChristoph Hellwig 	if (IS_ERR(dst_lock)) {
9124bdf87ebSChristoph Hellwig 		err = PTR_ERR(dst_lock);
9134bdf87ebSChristoph Hellwig 		goto out_put_src_lock;
9144bdf87ebSChristoph Hellwig 	}
9154bdf87ebSChristoph Hellwig 
9164bdf87ebSChristoph Hellwig 	dst_exception.inode = file_inode(dst_f);
9174bdf87ebSChristoph Hellwig 	dst_exception.state = dst_lock->open_context->state;
9184bdf87ebSChristoph Hellwig 
919e5341f3aSPeng Tao 	do {
9204bdf87ebSChristoph Hellwig 		err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock,
9214bdf87ebSChristoph Hellwig 					src_offset, dst_offset, count);
922e5341f3aSPeng Tao 		if (err == -ENOTSUPP || err == -EOPNOTSUPP) {
923e5341f3aSPeng Tao 			NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE;
9244bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
9254bdf87ebSChristoph Hellwig 			break;
926e5341f3aSPeng Tao 		}
927e5341f3aSPeng Tao 
9284bdf87ebSChristoph Hellwig 		err2 = nfs4_handle_exception(server, err, &src_exception);
9294bdf87ebSChristoph Hellwig 		err = nfs4_handle_exception(server, err, &dst_exception);
9304bdf87ebSChristoph Hellwig 		if (!err)
9314bdf87ebSChristoph Hellwig 			err = err2;
9324bdf87ebSChristoph Hellwig 	} while (src_exception.retry || dst_exception.retry);
9334bdf87ebSChristoph Hellwig 
9344bdf87ebSChristoph Hellwig 	nfs_put_lock_context(dst_lock);
9354bdf87ebSChristoph Hellwig out_put_src_lock:
9364bdf87ebSChristoph Hellwig 	nfs_put_lock_context(src_lock);
937e5341f3aSPeng Tao 	return err;
938e5341f3aSPeng Tao }
939