xref: /openbmc/linux/fs/nfs/nfs42proc.c (revision 638037b1)
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"
20638037b1STrond Myklebust #include "nfs4trace.h"
211b4a4bd8SPeng Tao 
22291e1b94SAnna Schumaker #define NFSDBG_FACILITY NFSDBG_PROC
23c975c209SOlga Kornievskaia static int nfs42_do_offload_cancel_async(struct file *dst, nfs4_stateid *std);
241c6dcbe5SAnna Schumaker 
250491567bSOlga Kornievskaia static void nfs42_set_netaddr(struct file *filep, struct nfs42_netaddr *naddr)
260491567bSOlga Kornievskaia {
270491567bSOlga Kornievskaia 	struct nfs_client *clp = (NFS_SERVER(file_inode(filep)))->nfs_client;
280491567bSOlga Kornievskaia 	unsigned short port = 2049;
290491567bSOlga Kornievskaia 
300491567bSOlga Kornievskaia 	rcu_read_lock();
310491567bSOlga Kornievskaia 	naddr->netid_len = scnprintf(naddr->netid,
320491567bSOlga Kornievskaia 					sizeof(naddr->netid), "%s",
330491567bSOlga Kornievskaia 					rpc_peeraddr2str(clp->cl_rpcclient,
340491567bSOlga Kornievskaia 					RPC_DISPLAY_NETID));
350491567bSOlga Kornievskaia 	naddr->addr_len = scnprintf(naddr->addr,
360491567bSOlga Kornievskaia 					sizeof(naddr->addr),
370491567bSOlga Kornievskaia 					"%s.%u.%u",
380491567bSOlga Kornievskaia 					rpc_peeraddr2str(clp->cl_rpcclient,
390491567bSOlga Kornievskaia 					RPC_DISPLAY_ADDR),
400491567bSOlga Kornievskaia 					port >> 8, port & 255);
410491567bSOlga Kornievskaia 	rcu_read_unlock();
420491567bSOlga Kornievskaia }
430491567bSOlga Kornievskaia 
44f4ac1674SAnna Schumaker static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
454bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, loff_t len)
46f4ac1674SAnna Schumaker {
47f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
489a51940bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
49f4ac1674SAnna Schumaker 	struct nfs42_falloc_args args = {
50f4ac1674SAnna Schumaker 		.falloc_fh	= NFS_FH(inode),
51f4ac1674SAnna Schumaker 		.falloc_offset	= offset,
52f4ac1674SAnna Schumaker 		.falloc_length	= len,
53913eca1aSAnna Schumaker 		.falloc_bitmask	= nfs4_fattr_bitmap,
54f4ac1674SAnna Schumaker 	};
559a51940bSAnna Schumaker 	struct nfs42_falloc_res res = {
569a51940bSAnna Schumaker 		.falloc_server	= server,
579a51940bSAnna Schumaker 	};
58f4ac1674SAnna Schumaker 	int status;
59f4ac1674SAnna Schumaker 
60f4ac1674SAnna Schumaker 	msg->rpc_argp = &args;
61f4ac1674SAnna Schumaker 	msg->rpc_resp = &res;
62f4ac1674SAnna Schumaker 
634bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.falloc_stateid, lock->open_context,
644bdf87ebSChristoph Hellwig 			lock, FMODE_WRITE);
65d826e5b8SOlga Kornievskaia 	if (status) {
66d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
67d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
68f4ac1674SAnna Schumaker 		return status;
69d826e5b8SOlga Kornievskaia 	}
70f4ac1674SAnna Schumaker 
719a51940bSAnna Schumaker 	res.falloc_fattr = nfs_alloc_fattr();
729a51940bSAnna Schumaker 	if (!res.falloc_fattr)
739a51940bSAnna Schumaker 		return -ENOMEM;
749a51940bSAnna Schumaker 
759a51940bSAnna Schumaker 	status = nfs4_call_sync(server->client, server, msg,
76f4ac1674SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
779a51940bSAnna Schumaker 	if (status == 0)
789a51940bSAnna Schumaker 		status = nfs_post_op_update_inode(inode, res.falloc_fattr);
799a51940bSAnna Schumaker 
809a51940bSAnna Schumaker 	kfree(res.falloc_fattr);
819a51940bSAnna Schumaker 	return status;
82f4ac1674SAnna Schumaker }
83f4ac1674SAnna Schumaker 
84f4ac1674SAnna Schumaker static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
85f4ac1674SAnna Schumaker 				loff_t offset, loff_t len)
86f4ac1674SAnna Schumaker {
87f4ac1674SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
88f4ac1674SAnna Schumaker 	struct nfs4_exception exception = { };
894bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
90f4ac1674SAnna Schumaker 	int err;
91f4ac1674SAnna Schumaker 
924bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
934bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
944bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
954bdf87ebSChristoph Hellwig 
964bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
974bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
984bdf87ebSChristoph Hellwig 
99f4ac1674SAnna Schumaker 	do {
1004bdf87ebSChristoph Hellwig 		err = _nfs42_proc_fallocate(msg, filep, lock, offset, len);
1014bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
1024bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
1034bdf87ebSChristoph Hellwig 			break;
1044bdf87ebSChristoph Hellwig 		}
105f4ac1674SAnna Schumaker 		err = nfs4_handle_exception(server, err, &exception);
106f4ac1674SAnna Schumaker 	} while (exception.retry);
107f4ac1674SAnna Schumaker 
1084bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
109f4ac1674SAnna Schumaker 	return err;
110f4ac1674SAnna Schumaker }
111f4ac1674SAnna Schumaker 
112f4ac1674SAnna Schumaker int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
113f4ac1674SAnna Schumaker {
114f4ac1674SAnna Schumaker 	struct rpc_message msg = {
115f4ac1674SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE],
116f4ac1674SAnna Schumaker 	};
117f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
118f4ac1674SAnna Schumaker 	int err;
119f4ac1674SAnna Schumaker 
120f4ac1674SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE))
121f4ac1674SAnna Schumaker 		return -EOPNOTSUPP;
122f4ac1674SAnna Schumaker 
1235955102cSAl Viro 	inode_lock(inode);
124f830f7ddSAnna Schumaker 
125f4ac1674SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
126f4ac1674SAnna Schumaker 	if (err == -EOPNOTSUPP)
127f4ac1674SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE;
128f830f7ddSAnna Schumaker 
1295955102cSAl Viro 	inode_unlock(inode);
130f4ac1674SAnna Schumaker 	return err;
131f4ac1674SAnna Schumaker }
132f4ac1674SAnna Schumaker 
133624bd5b7SAnna Schumaker int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
134624bd5b7SAnna Schumaker {
135624bd5b7SAnna Schumaker 	struct rpc_message msg = {
136624bd5b7SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE],
137624bd5b7SAnna Schumaker 	};
138624bd5b7SAnna Schumaker 	struct inode *inode = file_inode(filep);
139624bd5b7SAnna Schumaker 	int err;
140624bd5b7SAnna Schumaker 
141624bd5b7SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE))
142624bd5b7SAnna Schumaker 		return -EOPNOTSUPP;
143624bd5b7SAnna Schumaker 
1445955102cSAl Viro 	inode_lock(inode);
1451e564d3dSTrond Myklebust 	err = nfs_sync_inode(inode);
1461e564d3dSTrond Myklebust 	if (err)
1471e564d3dSTrond Myklebust 		goto out_unlock;
148f830f7ddSAnna Schumaker 
149624bd5b7SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
1509a51940bSAnna Schumaker 	if (err == 0)
1519a51940bSAnna Schumaker 		truncate_pagecache_range(inode, offset, (offset + len) -1);
152624bd5b7SAnna Schumaker 	if (err == -EOPNOTSUPP)
153624bd5b7SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE;
1541e564d3dSTrond Myklebust out_unlock:
1555955102cSAl Viro 	inode_unlock(inode);
156624bd5b7SAnna Schumaker 	return err;
157624bd5b7SAnna Schumaker }
158624bd5b7SAnna Schumaker 
15962164f31SOlga Kornievskaia static int handle_async_copy(struct nfs42_copy_res *res,
1600e65a32cSOlga Kornievskaia 			     struct nfs_server *dst_server,
1610e65a32cSOlga Kornievskaia 			     struct nfs_server *src_server,
16262164f31SOlga Kornievskaia 			     struct file *src,
16362164f31SOlga Kornievskaia 			     struct file *dst,
1640e65a32cSOlga Kornievskaia 			     nfs4_stateid *src_stateid,
1650e65a32cSOlga Kornievskaia 			     bool *restart)
16662164f31SOlga Kornievskaia {
16799f2c555SOlga Kornievskaia 	struct nfs4_copy_state *copy, *tmp_copy;
16862164f31SOlga Kornievskaia 	int status = NFS4_OK;
169bc0c9079SOlga Kornievskaia 	bool found_pending = false;
1700e65a32cSOlga Kornievskaia 	struct nfs_open_context *dst_ctx = nfs_file_open_context(dst);
1710e65a32cSOlga Kornievskaia 	struct nfs_open_context *src_ctx = nfs_file_open_context(src);
172bc0c9079SOlga Kornievskaia 
17399f2c555SOlga Kornievskaia 	copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
17499f2c555SOlga Kornievskaia 	if (!copy)
17599f2c555SOlga Kornievskaia 		return -ENOMEM;
17699f2c555SOlga Kornievskaia 
1770e65a32cSOlga Kornievskaia 	spin_lock(&dst_server->nfs_client->cl_lock);
1780e65a32cSOlga Kornievskaia 	list_for_each_entry(tmp_copy,
1790e65a32cSOlga Kornievskaia 				&dst_server->nfs_client->pending_cb_stateids,
180bc0c9079SOlga Kornievskaia 				copies) {
18199f2c555SOlga Kornievskaia 		if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
182bc0c9079SOlga Kornievskaia 				NFS4_STATEID_SIZE))
183bc0c9079SOlga Kornievskaia 			continue;
184bc0c9079SOlga Kornievskaia 		found_pending = true;
18599f2c555SOlga Kornievskaia 		list_del(&tmp_copy->copies);
186bc0c9079SOlga Kornievskaia 		break;
187bc0c9079SOlga Kornievskaia 	}
188bc0c9079SOlga Kornievskaia 	if (found_pending) {
1890e65a32cSOlga Kornievskaia 		spin_unlock(&dst_server->nfs_client->cl_lock);
19099f2c555SOlga Kornievskaia 		kfree(copy);
19199f2c555SOlga Kornievskaia 		copy = tmp_copy;
192bc0c9079SOlga Kornievskaia 		goto out;
193bc0c9079SOlga Kornievskaia 	}
19462164f31SOlga Kornievskaia 
19562164f31SOlga Kornievskaia 	memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
19662164f31SOlga Kornievskaia 	init_completion(&copy->completion);
1970e65a32cSOlga Kornievskaia 	copy->parent_dst_state = dst_ctx->state;
1980e65a32cSOlga Kornievskaia 	copy->parent_src_state = src_ctx->state;
19962164f31SOlga Kornievskaia 
2000e65a32cSOlga Kornievskaia 	list_add_tail(&copy->copies, &dst_server->ss_copies);
2010e65a32cSOlga Kornievskaia 	spin_unlock(&dst_server->nfs_client->cl_lock);
2020e65a32cSOlga Kornievskaia 
2030e65a32cSOlga Kornievskaia 	if (dst_server != src_server) {
2040e65a32cSOlga Kornievskaia 		spin_lock(&src_server->nfs_client->cl_lock);
2050e65a32cSOlga Kornievskaia 		list_add_tail(&copy->src_copies, &src_server->ss_copies);
2060e65a32cSOlga Kornievskaia 		spin_unlock(&src_server->nfs_client->cl_lock);
2070e65a32cSOlga Kornievskaia 	}
20862164f31SOlga Kornievskaia 
209c975c209SOlga Kornievskaia 	status = wait_for_completion_interruptible(&copy->completion);
2100e65a32cSOlga Kornievskaia 	spin_lock(&dst_server->nfs_client->cl_lock);
21162164f31SOlga Kornievskaia 	list_del_init(&copy->copies);
2120e65a32cSOlga Kornievskaia 	spin_unlock(&dst_server->nfs_client->cl_lock);
2130e65a32cSOlga Kornievskaia 	if (dst_server != src_server) {
2140e65a32cSOlga Kornievskaia 		spin_lock(&src_server->nfs_client->cl_lock);
2150e65a32cSOlga Kornievskaia 		list_del_init(&copy->src_copies);
2160e65a32cSOlga Kornievskaia 		spin_unlock(&src_server->nfs_client->cl_lock);
2170e65a32cSOlga Kornievskaia 	}
218c975c209SOlga Kornievskaia 	if (status == -ERESTARTSYS) {
219e4648aa4SOlga Kornievskaia 		goto out_cancel;
2200e65a32cSOlga Kornievskaia 	} else if (copy->flags || copy->error == NFS4ERR_PARTNER_NO_AUTH) {
221e4648aa4SOlga Kornievskaia 		status = -EAGAIN;
2220e65a32cSOlga Kornievskaia 		*restart = true;
223e4648aa4SOlga Kornievskaia 		goto out_cancel;
224c975c209SOlga Kornievskaia 	}
225bc0c9079SOlga Kornievskaia out:
22662164f31SOlga Kornievskaia 	res->write_res.count = copy->count;
22762164f31SOlga Kornievskaia 	memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
22862164f31SOlga Kornievskaia 	status = -copy->error;
22962164f31SOlga Kornievskaia 
23012406025SOlga Kornievskaia out_free:
23162164f31SOlga Kornievskaia 	kfree(copy);
23262164f31SOlga Kornievskaia 	return status;
233e4648aa4SOlga Kornievskaia out_cancel:
234e4648aa4SOlga Kornievskaia 	nfs42_do_offload_cancel_async(dst, &copy->stateid);
23512406025SOlga Kornievskaia 	if (!nfs42_files_from_same_server(src, dst))
23612406025SOlga Kornievskaia 		nfs42_do_offload_cancel_async(src, src_stateid);
23712406025SOlga Kornievskaia 	goto out_free;
23862164f31SOlga Kornievskaia }
23962164f31SOlga Kornievskaia 
2406b8d84e2SOlga Kornievskaia static int process_copy_commit(struct file *dst, loff_t pos_dst,
2416b8d84e2SOlga Kornievskaia 			       struct nfs42_copy_res *res)
2426b8d84e2SOlga Kornievskaia {
2436b8d84e2SOlga Kornievskaia 	struct nfs_commitres cres;
2446b8d84e2SOlga Kornievskaia 	int status = -ENOMEM;
2456b8d84e2SOlga Kornievskaia 
2466b8d84e2SOlga Kornievskaia 	cres.verf = kzalloc(sizeof(struct nfs_writeverf), GFP_NOFS);
2476b8d84e2SOlga Kornievskaia 	if (!cres.verf)
2486b8d84e2SOlga Kornievskaia 		goto out;
2496b8d84e2SOlga Kornievskaia 
2506b8d84e2SOlga Kornievskaia 	status = nfs4_proc_commit(dst, pos_dst, res->write_res.count, &cres);
2516b8d84e2SOlga Kornievskaia 	if (status)
2526b8d84e2SOlga Kornievskaia 		goto out_free;
2536b8d84e2SOlga Kornievskaia 	if (nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
2546b8d84e2SOlga Kornievskaia 				    &cres.verf->verifier)) {
2556b8d84e2SOlga Kornievskaia 		dprintk("commit verf differs from copy verf\n");
2566b8d84e2SOlga Kornievskaia 		status = -EAGAIN;
2576b8d84e2SOlga Kornievskaia 	}
2586b8d84e2SOlga Kornievskaia out_free:
2596b8d84e2SOlga Kornievskaia 	kfree(cres.verf);
2606b8d84e2SOlga Kornievskaia out:
2616b8d84e2SOlga Kornievskaia 	return status;
2626b8d84e2SOlga Kornievskaia }
2636b8d84e2SOlga Kornievskaia 
2649d8cacbfSTrond Myklebust static ssize_t _nfs42_proc_copy(struct file *src,
2652e72448bSAnna Schumaker 				struct nfs_lock_context *src_lock,
2669d8cacbfSTrond Myklebust 				struct file *dst,
2672e72448bSAnna Schumaker 				struct nfs_lock_context *dst_lock,
2689d8cacbfSTrond Myklebust 				struct nfs42_copy_args *args,
2691d38f3f0SOlga Kornievskaia 				struct nfs42_copy_res *res,
2701d38f3f0SOlga Kornievskaia 				struct nl4_server *nss,
2710e65a32cSOlga Kornievskaia 				nfs4_stateid *cnr_stateid,
2720e65a32cSOlga Kornievskaia 				bool *restart)
2732e72448bSAnna Schumaker {
2742e72448bSAnna Schumaker 	struct rpc_message msg = {
2752e72448bSAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY],
2769d8cacbfSTrond Myklebust 		.rpc_argp = args,
2779d8cacbfSTrond Myklebust 		.rpc_resp = res,
2782e72448bSAnna Schumaker 	};
2792e72448bSAnna Schumaker 	struct inode *dst_inode = file_inode(dst);
2800e65a32cSOlga Kornievskaia 	struct inode *src_inode = file_inode(src);
2810e65a32cSOlga Kornievskaia 	struct nfs_server *dst_server = NFS_SERVER(dst_inode);
2820e65a32cSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(src_inode);
2839d8cacbfSTrond Myklebust 	loff_t pos_src = args->src_pos;
2849d8cacbfSTrond Myklebust 	loff_t pos_dst = args->dst_pos;
2859d8cacbfSTrond Myklebust 	size_t count = args->count;
2861ee48bddSOlga Kornievskaia 	ssize_t status;
2872e72448bSAnna Schumaker 
2881d38f3f0SOlga Kornievskaia 	if (nss) {
2891d38f3f0SOlga Kornievskaia 		args->cp_src = nss;
2901d38f3f0SOlga Kornievskaia 		nfs4_stateid_copy(&args->src_stateid, cnr_stateid);
2911d38f3f0SOlga Kornievskaia 	} else {
2921d38f3f0SOlga Kornievskaia 		status = nfs4_set_rw_stateid(&args->src_stateid,
2931d38f3f0SOlga Kornievskaia 				src_lock->open_context, src_lock, FMODE_READ);
294d826e5b8SOlga Kornievskaia 		if (status) {
295d826e5b8SOlga Kornievskaia 			if (status == -EAGAIN)
296d826e5b8SOlga Kornievskaia 				status = -NFS4ERR_BAD_STATEID;
2972e72448bSAnna Schumaker 			return status;
2981d38f3f0SOlga Kornievskaia 		}
299d826e5b8SOlga Kornievskaia 	}
300837bb1d7STrond Myklebust 	status = nfs_filemap_write_and_wait_range(file_inode(src)->i_mapping,
301837bb1d7STrond Myklebust 			pos_src, pos_src + (loff_t)count - 1);
302837bb1d7STrond Myklebust 	if (status)
303837bb1d7STrond Myklebust 		return status;
304837bb1d7STrond Myklebust 
3059d8cacbfSTrond Myklebust 	status = nfs4_set_rw_stateid(&args->dst_stateid, dst_lock->open_context,
3062e72448bSAnna Schumaker 				     dst_lock, FMODE_WRITE);
307d826e5b8SOlga Kornievskaia 	if (status) {
308d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
309d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
3102e72448bSAnna Schumaker 		return status;
311d826e5b8SOlga Kornievskaia 	}
3122e72448bSAnna Schumaker 
313837bb1d7STrond Myklebust 	status = nfs_sync_inode(dst_inode);
314837bb1d7STrond Myklebust 	if (status)
315837bb1d7STrond Myklebust 		return status;
316837bb1d7STrond Myklebust 
31762164f31SOlga Kornievskaia 	res->commit_res.verf = NULL;
31862164f31SOlga Kornievskaia 	if (args->sync) {
31962164f31SOlga Kornievskaia 		res->commit_res.verf =
32062164f31SOlga Kornievskaia 			kzalloc(sizeof(struct nfs_writeverf), GFP_NOFS);
321e0926934SOlga Kornievskaia 		if (!res->commit_res.verf)
322e0926934SOlga Kornievskaia 			return -ENOMEM;
32362164f31SOlga Kornievskaia 	}
3240e65a32cSOlga Kornievskaia 	set_bit(NFS_CLNT_SRC_SSC_COPY_STATE,
3250e65a32cSOlga Kornievskaia 		&src_lock->open_context->state->flags);
326e4648aa4SOlga Kornievskaia 	set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
327e4648aa4SOlga Kornievskaia 		&dst_lock->open_context->state->flags);
328e4648aa4SOlga Kornievskaia 
3290e65a32cSOlga Kornievskaia 	status = nfs4_call_sync(dst_server->client, dst_server, &msg,
3309d8cacbfSTrond Myklebust 				&args->seq_args, &res->seq_res, 0);
3312e72448bSAnna Schumaker 	if (status == -ENOTSUPP)
3320e65a32cSOlga Kornievskaia 		dst_server->caps &= ~NFS_CAP_COPY;
3332e72448bSAnna Schumaker 	if (status)
334e0926934SOlga Kornievskaia 		goto out;
3352e72448bSAnna Schumaker 
33662164f31SOlga Kornievskaia 	if (args->sync &&
33762164f31SOlga Kornievskaia 		nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
338e0926934SOlga Kornievskaia 				    &res->commit_res.verf->verifier)) {
339e0926934SOlga Kornievskaia 		status = -EAGAIN;
340e0926934SOlga Kornievskaia 		goto out;
3412e72448bSAnna Schumaker 	}
3422e72448bSAnna Schumaker 
34362164f31SOlga Kornievskaia 	if (!res->synchronous) {
3440e65a32cSOlga Kornievskaia 		status = handle_async_copy(res, dst_server, src_server, src,
3450e65a32cSOlga Kornievskaia 				dst, &args->src_stateid, restart);
34662164f31SOlga Kornievskaia 		if (status)
347123c23c6SWenwen Wang 			goto out;
34862164f31SOlga Kornievskaia 	}
34962164f31SOlga Kornievskaia 
3506b8d84e2SOlga Kornievskaia 	if ((!res->synchronous || !args->sync) &&
3516b8d84e2SOlga Kornievskaia 			res->write_res.verifier.committed != NFS_FILE_SYNC) {
3526b8d84e2SOlga Kornievskaia 		status = process_copy_commit(dst, pos_dst, res);
3536b8d84e2SOlga Kornievskaia 		if (status)
354123c23c6SWenwen Wang 			goto out;
3556b8d84e2SOlga Kornievskaia 	}
3566b8d84e2SOlga Kornievskaia 
3572e72448bSAnna Schumaker 	truncate_pagecache_range(dst_inode, pos_dst,
3589d8cacbfSTrond Myklebust 				 pos_dst + res->write_res.count);
3592e72448bSAnna Schumaker 
360e0926934SOlga Kornievskaia 	status = res->write_res.count;
361e0926934SOlga Kornievskaia out:
36262164f31SOlga Kornievskaia 	if (args->sync)
363e0926934SOlga Kornievskaia 		kfree(res->commit_res.verf);
364e0926934SOlga Kornievskaia 	return status;
3652e72448bSAnna Schumaker }
3662e72448bSAnna Schumaker 
3672e72448bSAnna Schumaker ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
3681d38f3f0SOlga Kornievskaia 			struct file *dst, loff_t pos_dst, size_t count,
3691d38f3f0SOlga Kornievskaia 			struct nl4_server *nss,
37012751010SOlga Kornievskaia 			nfs4_stateid *cnr_stateid, bool sync)
3712e72448bSAnna Schumaker {
3722e72448bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(dst));
3732e72448bSAnna Schumaker 	struct nfs_lock_context *src_lock;
3742e72448bSAnna Schumaker 	struct nfs_lock_context *dst_lock;
3759d8cacbfSTrond Myklebust 	struct nfs42_copy_args args = {
3769d8cacbfSTrond Myklebust 		.src_fh		= NFS_FH(file_inode(src)),
3779d8cacbfSTrond Myklebust 		.src_pos	= pos_src,
3789d8cacbfSTrond Myklebust 		.dst_fh		= NFS_FH(file_inode(dst)),
3799d8cacbfSTrond Myklebust 		.dst_pos	= pos_dst,
3809d8cacbfSTrond Myklebust 		.count		= count,
38112751010SOlga Kornievskaia 		.sync		= sync,
3829d8cacbfSTrond Myklebust 	};
3839d8cacbfSTrond Myklebust 	struct nfs42_copy_res res;
3849d8cacbfSTrond Myklebust 	struct nfs4_exception src_exception = {
3859d8cacbfSTrond Myklebust 		.inode		= file_inode(src),
3869d8cacbfSTrond Myklebust 		.stateid	= &args.src_stateid,
3879d8cacbfSTrond Myklebust 	};
3889d8cacbfSTrond Myklebust 	struct nfs4_exception dst_exception = {
3899d8cacbfSTrond Myklebust 		.inode		= file_inode(dst),
3909d8cacbfSTrond Myklebust 		.stateid	= &args.dst_stateid,
3919d8cacbfSTrond Myklebust 	};
3922e72448bSAnna Schumaker 	ssize_t err, err2;
3930e65a32cSOlga Kornievskaia 	bool restart = false;
3942e72448bSAnna Schumaker 
3952e72448bSAnna Schumaker 	src_lock = nfs_get_lock_context(nfs_file_open_context(src));
3962e72448bSAnna Schumaker 	if (IS_ERR(src_lock))
3972e72448bSAnna Schumaker 		return PTR_ERR(src_lock);
3982e72448bSAnna Schumaker 
3992e72448bSAnna Schumaker 	src_exception.state = src_lock->open_context->state;
4002e72448bSAnna Schumaker 
4012e72448bSAnna Schumaker 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst));
4022e72448bSAnna Schumaker 	if (IS_ERR(dst_lock)) {
4032e72448bSAnna Schumaker 		err = PTR_ERR(dst_lock);
4042e72448bSAnna Schumaker 		goto out_put_src_lock;
4052e72448bSAnna Schumaker 	}
4062e72448bSAnna Schumaker 
4072e72448bSAnna Schumaker 	dst_exception.state = dst_lock->open_context->state;
4082e72448bSAnna Schumaker 
4092e72448bSAnna Schumaker 	do {
410ea8ea737SLinus Torvalds 		inode_lock(file_inode(dst));
4119d8cacbfSTrond Myklebust 		err = _nfs42_proc_copy(src, src_lock,
4129d8cacbfSTrond Myklebust 				dst, dst_lock,
4131d38f3f0SOlga Kornievskaia 				&args, &res,
4140e65a32cSOlga Kornievskaia 				nss, cnr_stateid, &restart);
415ea8ea737SLinus Torvalds 		inode_unlock(file_inode(dst));
4162e72448bSAnna Schumaker 
4179d8cacbfSTrond Myklebust 		if (err >= 0)
4189d8cacbfSTrond Myklebust 			break;
41912406025SOlga Kornievskaia 		if (err == -ENOTSUPP &&
42012406025SOlga Kornievskaia 				nfs42_files_from_same_server(src, dst)) {
4212e72448bSAnna Schumaker 			err = -EOPNOTSUPP;
4222e72448bSAnna Schumaker 			break;
423539f57b3SOlga Kornievskaia 		} else if (err == -EAGAIN) {
4240e65a32cSOlga Kornievskaia 			if (!restart) {
425539f57b3SOlga Kornievskaia 				dst_exception.retry = 1;
426539f57b3SOlga Kornievskaia 				continue;
4270e65a32cSOlga Kornievskaia 			}
4280e65a32cSOlga Kornievskaia 			break;
429539f57b3SOlga Kornievskaia 		} else if (err == -NFS4ERR_OFFLOAD_NO_REQS && !args.sync) {
430539f57b3SOlga Kornievskaia 			args.sync = true;
431e0926934SOlga Kornievskaia 			dst_exception.retry = 1;
432e0926934SOlga Kornievskaia 			continue;
4336b61c969SOlga Kornievskaia 		} else if ((err == -ESTALE ||
43412406025SOlga Kornievskaia 				err == -NFS4ERR_OFFLOAD_DENIED ||
43512406025SOlga Kornievskaia 				err == -ENOTSUPP) &&
4367e350197SOlga Kornievskaia 				!nfs42_files_from_same_server(src, dst)) {
4377e350197SOlga Kornievskaia 			nfs42_do_offload_cancel_async(src, &args.src_stateid);
4387e350197SOlga Kornievskaia 			err = -EOPNOTSUPP;
4397e350197SOlga Kornievskaia 			break;
4402e72448bSAnna Schumaker 		}
4412e72448bSAnna Schumaker 
4422e72448bSAnna Schumaker 		err2 = nfs4_handle_exception(server, err, &src_exception);
4432e72448bSAnna Schumaker 		err  = nfs4_handle_exception(server, err, &dst_exception);
4442e72448bSAnna Schumaker 		if (!err)
4452e72448bSAnna Schumaker 			err = err2;
4462e72448bSAnna Schumaker 	} while (src_exception.retry || dst_exception.retry);
4472e72448bSAnna Schumaker 
4482e72448bSAnna Schumaker 	nfs_put_lock_context(dst_lock);
4492e72448bSAnna Schumaker out_put_src_lock:
4502e72448bSAnna Schumaker 	nfs_put_lock_context(src_lock);
4512e72448bSAnna Schumaker 	return err;
4522e72448bSAnna Schumaker }
4532e72448bSAnna Schumaker 
454c975c209SOlga Kornievskaia struct nfs42_offloadcancel_data {
455c975c209SOlga Kornievskaia 	struct nfs_server *seq_server;
456c975c209SOlga Kornievskaia 	struct nfs42_offload_status_args args;
457c975c209SOlga Kornievskaia 	struct nfs42_offload_status_res res;
458c975c209SOlga Kornievskaia };
459c975c209SOlga Kornievskaia 
460c975c209SOlga Kornievskaia static void nfs42_offload_cancel_prepare(struct rpc_task *task, void *calldata)
461c975c209SOlga Kornievskaia {
462c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
463c975c209SOlga Kornievskaia 
464c975c209SOlga Kornievskaia 	nfs4_setup_sequence(data->seq_server->nfs_client,
465c975c209SOlga Kornievskaia 				&data->args.osa_seq_args,
466c975c209SOlga Kornievskaia 				&data->res.osr_seq_res, task);
467c975c209SOlga Kornievskaia }
468c975c209SOlga Kornievskaia 
469c975c209SOlga Kornievskaia static void nfs42_offload_cancel_done(struct rpc_task *task, void *calldata)
470c975c209SOlga Kornievskaia {
471c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
472c975c209SOlga Kornievskaia 
473c975c209SOlga Kornievskaia 	nfs41_sequence_done(task, &data->res.osr_seq_res);
474c975c209SOlga Kornievskaia 	if (task->tk_status &&
475c975c209SOlga Kornievskaia 		nfs4_async_handle_error(task, data->seq_server, NULL,
476c975c209SOlga Kornievskaia 			NULL) == -EAGAIN)
477c975c209SOlga Kornievskaia 		rpc_restart_call_prepare(task);
478c975c209SOlga Kornievskaia }
479c975c209SOlga Kornievskaia 
480c975c209SOlga Kornievskaia static void nfs42_free_offloadcancel_data(void *data)
481c975c209SOlga Kornievskaia {
482c975c209SOlga Kornievskaia 	kfree(data);
483c975c209SOlga Kornievskaia }
484c975c209SOlga Kornievskaia 
485c975c209SOlga Kornievskaia static const struct rpc_call_ops nfs42_offload_cancel_ops = {
486c975c209SOlga Kornievskaia 	.rpc_call_prepare = nfs42_offload_cancel_prepare,
487c975c209SOlga Kornievskaia 	.rpc_call_done = nfs42_offload_cancel_done,
488c975c209SOlga Kornievskaia 	.rpc_release = nfs42_free_offloadcancel_data,
489c975c209SOlga Kornievskaia };
490c975c209SOlga Kornievskaia 
491c975c209SOlga Kornievskaia static int nfs42_do_offload_cancel_async(struct file *dst,
492c975c209SOlga Kornievskaia 					 nfs4_stateid *stateid)
493c975c209SOlga Kornievskaia {
494c975c209SOlga Kornievskaia 	struct nfs_server *dst_server = NFS_SERVER(file_inode(dst));
495c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = NULL;
496c975c209SOlga Kornievskaia 	struct nfs_open_context *ctx = nfs_file_open_context(dst);
497c975c209SOlga Kornievskaia 	struct rpc_task *task;
498c975c209SOlga Kornievskaia 	struct rpc_message msg = {
499c975c209SOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OFFLOAD_CANCEL],
500c975c209SOlga Kornievskaia 		.rpc_cred = ctx->cred,
501c975c209SOlga Kornievskaia 	};
502c975c209SOlga Kornievskaia 	struct rpc_task_setup task_setup_data = {
503c975c209SOlga Kornievskaia 		.rpc_client = dst_server->client,
504c975c209SOlga Kornievskaia 		.rpc_message = &msg,
505c975c209SOlga Kornievskaia 		.callback_ops = &nfs42_offload_cancel_ops,
506c975c209SOlga Kornievskaia 		.workqueue = nfsiod_workqueue,
507c975c209SOlga Kornievskaia 		.flags = RPC_TASK_ASYNC,
508c975c209SOlga Kornievskaia 	};
509c975c209SOlga Kornievskaia 	int status;
510c975c209SOlga Kornievskaia 
511c975c209SOlga Kornievskaia 	if (!(dst_server->caps & NFS_CAP_OFFLOAD_CANCEL))
512c975c209SOlga Kornievskaia 		return -EOPNOTSUPP;
513c975c209SOlga Kornievskaia 
514c975c209SOlga Kornievskaia 	data = kzalloc(sizeof(struct nfs42_offloadcancel_data), GFP_NOFS);
515c975c209SOlga Kornievskaia 	if (data == NULL)
516c975c209SOlga Kornievskaia 		return -ENOMEM;
517c975c209SOlga Kornievskaia 
518c975c209SOlga Kornievskaia 	data->seq_server = dst_server;
519c975c209SOlga Kornievskaia 	data->args.osa_src_fh = NFS_FH(file_inode(dst));
520c975c209SOlga Kornievskaia 	memcpy(&data->args.osa_stateid, stateid,
521c975c209SOlga Kornievskaia 		sizeof(data->args.osa_stateid));
522c975c209SOlga Kornievskaia 	msg.rpc_argp = &data->args;
523c975c209SOlga Kornievskaia 	msg.rpc_resp = &data->res;
524c975c209SOlga Kornievskaia 	task_setup_data.callback_data = data;
525c975c209SOlga Kornievskaia 	nfs4_init_sequence(&data->args.osa_seq_args, &data->res.osr_seq_res,
526c975c209SOlga Kornievskaia 			   1, 0);
527c975c209SOlga Kornievskaia 	task = rpc_run_task(&task_setup_data);
528c975c209SOlga Kornievskaia 	if (IS_ERR(task))
529c975c209SOlga Kornievskaia 		return PTR_ERR(task);
530c975c209SOlga Kornievskaia 	status = rpc_wait_for_completion_task(task);
531c975c209SOlga Kornievskaia 	if (status == -ENOTSUPP)
532c975c209SOlga Kornievskaia 		dst_server->caps &= ~NFS_CAP_OFFLOAD_CANCEL;
533c975c209SOlga Kornievskaia 	rpc_put_task(task);
534c975c209SOlga Kornievskaia 	return status;
535c975c209SOlga Kornievskaia }
536c975c209SOlga Kornievskaia 
53700030104SYueHaibing static int _nfs42_proc_copy_notify(struct file *src, struct file *dst,
5380491567bSOlga Kornievskaia 				   struct nfs42_copy_notify_args *args,
5390491567bSOlga Kornievskaia 				   struct nfs42_copy_notify_res *res)
5400491567bSOlga Kornievskaia {
5410491567bSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
5420491567bSOlga Kornievskaia 	struct rpc_message msg = {
5430491567bSOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY_NOTIFY],
5440491567bSOlga Kornievskaia 		.rpc_argp = args,
5450491567bSOlga Kornievskaia 		.rpc_resp = res,
5460491567bSOlga Kornievskaia 	};
5470491567bSOlga Kornievskaia 	int status;
5480491567bSOlga Kornievskaia 	struct nfs_open_context *ctx;
5490491567bSOlga Kornievskaia 	struct nfs_lock_context *l_ctx;
5500491567bSOlga Kornievskaia 
5510491567bSOlga Kornievskaia 	ctx = get_nfs_open_context(nfs_file_open_context(src));
5520491567bSOlga Kornievskaia 	l_ctx = nfs_get_lock_context(ctx);
5530491567bSOlga Kornievskaia 	if (IS_ERR(l_ctx))
5540491567bSOlga Kornievskaia 		return PTR_ERR(l_ctx);
5550491567bSOlga Kornievskaia 
5560491567bSOlga Kornievskaia 	status = nfs4_set_rw_stateid(&args->cna_src_stateid, ctx, l_ctx,
5570491567bSOlga Kornievskaia 				     FMODE_READ);
5580491567bSOlga Kornievskaia 	nfs_put_lock_context(l_ctx);
559d826e5b8SOlga Kornievskaia 	if (status) {
560d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
561d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
5620491567bSOlga Kornievskaia 		return status;
563d826e5b8SOlga Kornievskaia 	}
5640491567bSOlga Kornievskaia 
5650491567bSOlga Kornievskaia 	status = nfs4_call_sync(src_server->client, src_server, &msg,
5660491567bSOlga Kornievskaia 				&args->cna_seq_args, &res->cnr_seq_res, 0);
5670491567bSOlga Kornievskaia 	if (status == -ENOTSUPP)
5680491567bSOlga Kornievskaia 		src_server->caps &= ~NFS_CAP_COPY_NOTIFY;
5690491567bSOlga Kornievskaia 
5700491567bSOlga Kornievskaia 	put_nfs_open_context(nfs_file_open_context(src));
5710491567bSOlga Kornievskaia 	return status;
5720491567bSOlga Kornievskaia }
5730491567bSOlga Kornievskaia 
5740491567bSOlga Kornievskaia int nfs42_proc_copy_notify(struct file *src, struct file *dst,
5750491567bSOlga Kornievskaia 				struct nfs42_copy_notify_res *res)
5760491567bSOlga Kornievskaia {
5770491567bSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
5780491567bSOlga Kornievskaia 	struct nfs42_copy_notify_args *args;
5790491567bSOlga Kornievskaia 	struct nfs4_exception exception = {
5800491567bSOlga Kornievskaia 		.inode = file_inode(src),
5810491567bSOlga Kornievskaia 	};
5820491567bSOlga Kornievskaia 	int status;
5830491567bSOlga Kornievskaia 
5840491567bSOlga Kornievskaia 	if (!(src_server->caps & NFS_CAP_COPY_NOTIFY))
5850491567bSOlga Kornievskaia 		return -EOPNOTSUPP;
5860491567bSOlga Kornievskaia 
5870491567bSOlga Kornievskaia 	args = kzalloc(sizeof(struct nfs42_copy_notify_args), GFP_NOFS);
5880491567bSOlga Kornievskaia 	if (args == NULL)
5890491567bSOlga Kornievskaia 		return -ENOMEM;
5900491567bSOlga Kornievskaia 
5910491567bSOlga Kornievskaia 	args->cna_src_fh  = NFS_FH(file_inode(src)),
5920491567bSOlga Kornievskaia 	args->cna_dst.nl4_type = NL4_NETADDR;
5930491567bSOlga Kornievskaia 	nfs42_set_netaddr(dst, &args->cna_dst.u.nl4_addr);
5940491567bSOlga Kornievskaia 	exception.stateid = &args->cna_src_stateid;
5950491567bSOlga Kornievskaia 
5960491567bSOlga Kornievskaia 	do {
5970491567bSOlga Kornievskaia 		status = _nfs42_proc_copy_notify(src, dst, args, res);
5980491567bSOlga Kornievskaia 		if (status == -ENOTSUPP) {
5990491567bSOlga Kornievskaia 			status = -EOPNOTSUPP;
6000491567bSOlga Kornievskaia 			goto out;
6010491567bSOlga Kornievskaia 		}
6020491567bSOlga Kornievskaia 		status = nfs4_handle_exception(src_server, status, &exception);
6030491567bSOlga Kornievskaia 	} while (exception.retry);
6040491567bSOlga Kornievskaia 
6050491567bSOlga Kornievskaia out:
6060491567bSOlga Kornievskaia 	kfree(args);
6070491567bSOlga Kornievskaia 	return status;
6080491567bSOlga Kornievskaia }
6090491567bSOlga Kornievskaia 
6104bdf87ebSChristoph Hellwig static loff_t _nfs42_proc_llseek(struct file *filep,
6114bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, int whence)
6121c6dcbe5SAnna Schumaker {
6131c6dcbe5SAnna Schumaker 	struct inode *inode = file_inode(filep);
6141c6dcbe5SAnna Schumaker 	struct nfs42_seek_args args = {
6151c6dcbe5SAnna Schumaker 		.sa_fh		= NFS_FH(inode),
6161c6dcbe5SAnna Schumaker 		.sa_offset	= offset,
6171c6dcbe5SAnna Schumaker 		.sa_what	= (whence == SEEK_HOLE) ?
6181c6dcbe5SAnna Schumaker 					NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA,
6191c6dcbe5SAnna Schumaker 	};
6201c6dcbe5SAnna Schumaker 	struct nfs42_seek_res res;
6211c6dcbe5SAnna Schumaker 	struct rpc_message msg = {
6221c6dcbe5SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK],
6231c6dcbe5SAnna Schumaker 		.rpc_argp = &args,
6241c6dcbe5SAnna Schumaker 		.rpc_resp = &res,
6251c6dcbe5SAnna Schumaker 	};
6261c6dcbe5SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
6271c6dcbe5SAnna Schumaker 	int status;
6281c6dcbe5SAnna Schumaker 
629878ffa9fSAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_SEEK))
6301c6dcbe5SAnna Schumaker 		return -ENOTSUPP;
6311c6dcbe5SAnna Schumaker 
6324bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context,
6334bdf87ebSChristoph Hellwig 			lock, FMODE_READ);
634d826e5b8SOlga Kornievskaia 	if (status) {
635d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
636d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
6371c6dcbe5SAnna Schumaker 		return status;
638d826e5b8SOlga Kornievskaia 	}
6391c6dcbe5SAnna Schumaker 
640e95fc4a0STrond Myklebust 	status = nfs_filemap_write_and_wait_range(inode->i_mapping,
641e95fc4a0STrond Myklebust 			offset, LLONG_MAX);
642e95fc4a0STrond Myklebust 	if (status)
643e95fc4a0STrond Myklebust 		return status;
644e95fc4a0STrond Myklebust 
6451c6dcbe5SAnna Schumaker 	status = nfs4_call_sync(server->client, server, &msg,
6461c6dcbe5SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
6471c6dcbe5SAnna Schumaker 	if (status == -ENOTSUPP)
6481c6dcbe5SAnna Schumaker 		server->caps &= ~NFS_CAP_SEEK;
6491c6dcbe5SAnna Schumaker 	if (status)
6501c6dcbe5SAnna Schumaker 		return status;
6511c6dcbe5SAnna Schumaker 
6521c6dcbe5SAnna Schumaker 	return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes);
6531c6dcbe5SAnna Schumaker }
654be3a5d23STrond Myklebust 
655bdcc2cd1SJ. Bruce Fields loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
656bdcc2cd1SJ. Bruce Fields {
657bdcc2cd1SJ. Bruce Fields 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
658bdcc2cd1SJ. Bruce Fields 	struct nfs4_exception exception = { };
6594bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
660306a5549SJ. Bruce Fields 	loff_t err;
661bdcc2cd1SJ. Bruce Fields 
6624bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
6634bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
6644bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
6654bdf87ebSChristoph Hellwig 
6664bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
6674bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
6684bdf87ebSChristoph Hellwig 
669bdcc2cd1SJ. Bruce Fields 	do {
6704bdf87ebSChristoph Hellwig 		err = _nfs42_proc_llseek(filep, lock, offset, whence);
671306a5549SJ. Bruce Fields 		if (err >= 0)
672306a5549SJ. Bruce Fields 			break;
6734bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
6744bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
6754bdf87ebSChristoph Hellwig 			break;
6764bdf87ebSChristoph Hellwig 		}
677bdcc2cd1SJ. Bruce Fields 		err = nfs4_handle_exception(server, err, &exception);
678bdcc2cd1SJ. Bruce Fields 	} while (exception.retry);
679bdcc2cd1SJ. Bruce Fields 
6804bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
681bdcc2cd1SJ. Bruce Fields 	return err;
682bdcc2cd1SJ. Bruce Fields }
683bdcc2cd1SJ. Bruce Fields 
684bdcc2cd1SJ. Bruce Fields 
6851b4a4bd8SPeng Tao static void
6861b4a4bd8SPeng Tao nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata)
6871b4a4bd8SPeng Tao {
6881b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
6899a0fe867STrond Myklebust 	struct inode *inode = data->inode;
6909a0fe867STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
6919a0fe867STrond Myklebust 	struct pnfs_layout_hdr *lo;
6921b4a4bd8SPeng Tao 
6939a0fe867STrond Myklebust 	spin_lock(&inode->i_lock);
6949a0fe867STrond Myklebust 	lo = NFS_I(inode)->layout;
6959a0fe867STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
6969a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
6979a0fe867STrond Myklebust 		rpc_exit(task, 0);
6989a0fe867STrond Myklebust 		return;
6999a0fe867STrond Myklebust 	}
7009a0fe867STrond Myklebust 	nfs4_stateid_copy(&data->args.stateid, &lo->plh_stateid);
7019a0fe867STrond Myklebust 	spin_unlock(&inode->i_lock);
7026de7e12fSAnna Schumaker 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
7036de7e12fSAnna Schumaker 			    &data->res.seq_res, task);
7041b4a4bd8SPeng Tao }
7051b4a4bd8SPeng Tao 
7061b4a4bd8SPeng Tao static void
7071b4a4bd8SPeng Tao nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
7081b4a4bd8SPeng Tao {
7091b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
71068d264cfSPeng Tao 	struct inode *inode = data->inode;
71168d264cfSPeng Tao 	struct pnfs_layout_hdr *lo;
7121b4a4bd8SPeng Tao 
7131b4a4bd8SPeng Tao 	if (!nfs4_sequence_done(task, &data->res.seq_res))
7141b4a4bd8SPeng Tao 		return;
7151b4a4bd8SPeng Tao 
7166c5a0d89STrond Myklebust 	switch (task->tk_status) {
7176c5a0d89STrond Myklebust 	case 0:
718638037b1STrond Myklebust 		return;
719cf61eb26STrond Myklebust 	case -NFS4ERR_BADHANDLE:
720cf61eb26STrond Myklebust 	case -ESTALE:
721cf61eb26STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
722cf61eb26STrond Myklebust 		break;
72368d264cfSPeng Tao 	case -NFS4ERR_EXPIRED:
724206b3bb5STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
725206b3bb5STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
72668d264cfSPeng Tao 	case -NFS4ERR_STALE_STATEID:
72768d264cfSPeng Tao 	case -NFS4ERR_BAD_STATEID:
72868d264cfSPeng Tao 		spin_lock(&inode->i_lock);
72968d264cfSPeng Tao 		lo = NFS_I(inode)->layout;
7309a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
7319a0fe867STrond Myklebust 		    nfs4_stateid_match(&data->args.stateid,
73268d264cfSPeng Tao 					     &lo->plh_stateid)) {
73368d264cfSPeng Tao 			LIST_HEAD(head);
73468d264cfSPeng Tao 
73568d264cfSPeng Tao 			/*
73668d264cfSPeng Tao 			 * Mark the bad layout state as invalid, then retry
73768d264cfSPeng Tao 			 * with the current stateid.
73868d264cfSPeng Tao 			 */
7395f46be04STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
74068d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
74168d264cfSPeng Tao 			pnfs_free_lseg_list(&head);
7421f18b82cSTrond Myklebust 			nfs_commit_inode(inode, 0);
74368d264cfSPeng Tao 		} else
74468d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
74568d264cfSPeng Tao 		break;
7469a0fe867STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
7479a0fe867STrond Myklebust 		spin_lock(&inode->i_lock);
7489a0fe867STrond Myklebust 		lo = NFS_I(inode)->layout;
7499a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
7509a0fe867STrond Myklebust 		    nfs4_stateid_match_other(&data->args.stateid,
7519a0fe867STrond Myklebust 					&lo->plh_stateid)) {
7529a0fe867STrond Myklebust 			/* Do we need to delay before resending? */
7539a0fe867STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
7549a0fe867STrond Myklebust 						&data->args.stateid))
7559a0fe867STrond Myklebust 				rpc_delay(task, HZ);
7569a0fe867STrond Myklebust 			rpc_restart_call_prepare(task);
7579a0fe867STrond Myklebust 		}
7589a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
7599a0fe867STrond Myklebust 		break;
7606c5a0d89STrond Myklebust 	case -ENOTSUPP:
7616c5a0d89STrond Myklebust 	case -EOPNOTSUPP:
76268d264cfSPeng Tao 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
7631b4a4bd8SPeng Tao 	}
764638037b1STrond Myklebust 
765638037b1STrond Myklebust 	trace_nfs4_layoutstats(inode, &data->args.stateid, task->tk_status);
7666c5a0d89STrond Myklebust }
7671b4a4bd8SPeng Tao 
7681b4a4bd8SPeng Tao static void
7691b4a4bd8SPeng Tao nfs42_layoutstat_release(void *calldata)
7701b4a4bd8SPeng Tao {
7711b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
772422c93c8STrond Myklebust 	struct nfs42_layoutstat_devinfo *devinfo = data->args.devinfo;
773422c93c8STrond Myklebust 	int i;
7748733408dSPeng Tao 
775422c93c8STrond Myklebust 	for (i = 0; i < data->args.num_dev; i++) {
776422c93c8STrond Myklebust 		if (devinfo[i].ld_private.ops && devinfo[i].ld_private.ops->free)
777422c93c8STrond Myklebust 			devinfo[i].ld_private.ops->free(&devinfo[i].ld_private);
778422c93c8STrond Myklebust 	}
7791b4a4bd8SPeng Tao 
7801b4a4bd8SPeng Tao 	pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout);
7811bfe3b25SPeng Tao 	smp_mb__before_atomic();
7821bfe3b25SPeng Tao 	clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags);
7831bfe3b25SPeng Tao 	smp_mb__after_atomic();
7841b4a4bd8SPeng Tao 	nfs_iput_and_deactive(data->inode);
7851b4a4bd8SPeng Tao 	kfree(data->args.devinfo);
7861b4a4bd8SPeng Tao 	kfree(data);
7871b4a4bd8SPeng Tao }
7881b4a4bd8SPeng Tao 
789be3a5d23STrond Myklebust static const struct rpc_call_ops nfs42_layoutstat_ops = {
7901b4a4bd8SPeng Tao 	.rpc_call_prepare = nfs42_layoutstat_prepare,
7911b4a4bd8SPeng Tao 	.rpc_call_done = nfs42_layoutstat_done,
7921b4a4bd8SPeng Tao 	.rpc_release = nfs42_layoutstat_release,
793be3a5d23STrond Myklebust };
794be3a5d23STrond Myklebust 
795be3a5d23STrond Myklebust int nfs42_proc_layoutstats_generic(struct nfs_server *server,
796be3a5d23STrond Myklebust 				   struct nfs42_layoutstat_data *data)
797be3a5d23STrond Myklebust {
798be3a5d23STrond Myklebust 	struct rpc_message msg = {
799be3a5d23STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS],
800be3a5d23STrond Myklebust 		.rpc_argp = &data->args,
801be3a5d23STrond Myklebust 		.rpc_resp = &data->res,
802be3a5d23STrond Myklebust 	};
803be3a5d23STrond Myklebust 	struct rpc_task_setup task_setup = {
804be3a5d23STrond Myklebust 		.rpc_client = server->client,
805be3a5d23STrond Myklebust 		.rpc_message = &msg,
806be3a5d23STrond Myklebust 		.callback_ops = &nfs42_layoutstat_ops,
807be3a5d23STrond Myklebust 		.callback_data = data,
808be3a5d23STrond Myklebust 		.flags = RPC_TASK_ASYNC,
809be3a5d23STrond Myklebust 	};
810be3a5d23STrond Myklebust 	struct rpc_task *task;
811be3a5d23STrond Myklebust 
8121b4a4bd8SPeng Tao 	data->inode = nfs_igrab_and_active(data->args.inode);
8131b4a4bd8SPeng Tao 	if (!data->inode) {
8141b4a4bd8SPeng Tao 		nfs42_layoutstat_release(data);
8151b4a4bd8SPeng Tao 		return -EAGAIN;
8161b4a4bd8SPeng Tao 	}
817fba83f34SAnna Schumaker 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
818be3a5d23STrond Myklebust 	task = rpc_run_task(&task_setup);
819be3a5d23STrond Myklebust 	if (IS_ERR(task))
820be3a5d23STrond Myklebust 		return PTR_ERR(task);
8213f807e5aSJeff Layton 	rpc_put_task(task);
822be3a5d23STrond Myklebust 	return 0;
823be3a5d23STrond Myklebust }
824e5341f3aSPeng Tao 
8253eb86093STrond Myklebust static struct nfs42_layouterror_data *
8263eb86093STrond Myklebust nfs42_alloc_layouterror_data(struct pnfs_layout_segment *lseg, gfp_t gfp_flags)
8273eb86093STrond Myklebust {
8283eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
8293eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
8303eb86093STrond Myklebust 
8313eb86093STrond Myklebust 	data = kzalloc(sizeof(*data), gfp_flags);
8323eb86093STrond Myklebust 	if (data) {
8333eb86093STrond Myklebust 		data->args.inode = data->inode = nfs_igrab_and_active(inode);
8343eb86093STrond Myklebust 		if (data->inode) {
8353eb86093STrond Myklebust 			data->lseg = pnfs_get_lseg(lseg);
8363eb86093STrond Myklebust 			if (data->lseg)
8373eb86093STrond Myklebust 				return data;
8383eb86093STrond Myklebust 			nfs_iput_and_deactive(data->inode);
8393eb86093STrond Myklebust 		}
8403eb86093STrond Myklebust 		kfree(data);
8413eb86093STrond Myklebust 	}
8423eb86093STrond Myklebust 	return NULL;
8433eb86093STrond Myklebust }
8443eb86093STrond Myklebust 
8453eb86093STrond Myklebust static void
8463eb86093STrond Myklebust nfs42_free_layouterror_data(struct nfs42_layouterror_data *data)
8473eb86093STrond Myklebust {
8483eb86093STrond Myklebust 	pnfs_put_lseg(data->lseg);
8493eb86093STrond Myklebust 	nfs_iput_and_deactive(data->inode);
8503eb86093STrond Myklebust 	kfree(data);
8513eb86093STrond Myklebust }
8523eb86093STrond Myklebust 
8533eb86093STrond Myklebust static void
8543eb86093STrond Myklebust nfs42_layouterror_prepare(struct rpc_task *task, void *calldata)
8553eb86093STrond Myklebust {
8563eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
8573eb86093STrond Myklebust 	struct inode *inode = data->inode;
8583eb86093STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
8593eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
8603eb86093STrond Myklebust 	unsigned i;
8613eb86093STrond Myklebust 
8623eb86093STrond Myklebust 	spin_lock(&inode->i_lock);
8633eb86093STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
8643eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
8653eb86093STrond Myklebust 		rpc_exit(task, 0);
8663eb86093STrond Myklebust 		return;
8673eb86093STrond Myklebust 	}
8683eb86093STrond Myklebust 	for (i = 0; i < data->args.num_errors; i++)
8693eb86093STrond Myklebust 		nfs4_stateid_copy(&data->args.errors[i].stateid,
8703eb86093STrond Myklebust 				&lo->plh_stateid);
8713eb86093STrond Myklebust 	spin_unlock(&inode->i_lock);
8723eb86093STrond Myklebust 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
8733eb86093STrond Myklebust 			    &data->res.seq_res, task);
8743eb86093STrond Myklebust }
8753eb86093STrond Myklebust 
8763eb86093STrond Myklebust static void
8773eb86093STrond Myklebust nfs42_layouterror_done(struct rpc_task *task, void *calldata)
8783eb86093STrond Myklebust {
8793eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
8803eb86093STrond Myklebust 	struct inode *inode = data->inode;
8813eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
8823eb86093STrond Myklebust 
8833eb86093STrond Myklebust 	if (!nfs4_sequence_done(task, &data->res.seq_res))
8843eb86093STrond Myklebust 		return;
8853eb86093STrond Myklebust 
8863eb86093STrond Myklebust 	switch (task->tk_status) {
8873eb86093STrond Myklebust 	case 0:
888638037b1STrond Myklebust 		return;
8893eb86093STrond Myklebust 	case -NFS4ERR_BADHANDLE:
8903eb86093STrond Myklebust 	case -ESTALE:
8913eb86093STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
8923eb86093STrond Myklebust 		break;
8933eb86093STrond Myklebust 	case -NFS4ERR_EXPIRED:
8943eb86093STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
8953eb86093STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
8963eb86093STrond Myklebust 	case -NFS4ERR_STALE_STATEID:
8973eb86093STrond Myklebust 	case -NFS4ERR_BAD_STATEID:
8983eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
8993eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
9003eb86093STrond Myklebust 		    nfs4_stateid_match(&data->args.errors[0].stateid,
9013eb86093STrond Myklebust 					     &lo->plh_stateid)) {
9023eb86093STrond Myklebust 			LIST_HEAD(head);
9033eb86093STrond Myklebust 
9043eb86093STrond Myklebust 			/*
9053eb86093STrond Myklebust 			 * Mark the bad layout state as invalid, then retry
9063eb86093STrond Myklebust 			 * with the current stateid.
9073eb86093STrond Myklebust 			 */
9083eb86093STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
9093eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
9103eb86093STrond Myklebust 			pnfs_free_lseg_list(&head);
9113eb86093STrond Myklebust 			nfs_commit_inode(inode, 0);
9123eb86093STrond Myklebust 		} else
9133eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
9143eb86093STrond Myklebust 		break;
9153eb86093STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
9163eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
9173eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
9183eb86093STrond Myklebust 		    nfs4_stateid_match_other(&data->args.errors[0].stateid,
9193eb86093STrond Myklebust 					&lo->plh_stateid)) {
9203eb86093STrond Myklebust 			/* Do we need to delay before resending? */
9213eb86093STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
9223eb86093STrond Myklebust 						&data->args.errors[0].stateid))
9233eb86093STrond Myklebust 				rpc_delay(task, HZ);
9243eb86093STrond Myklebust 			rpc_restart_call_prepare(task);
9253eb86093STrond Myklebust 		}
9263eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
9273eb86093STrond Myklebust 		break;
9283eb86093STrond Myklebust 	case -ENOTSUPP:
9293eb86093STrond Myklebust 	case -EOPNOTSUPP:
9303eb86093STrond Myklebust 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTERROR;
9313eb86093STrond Myklebust 	}
932638037b1STrond Myklebust 
933638037b1STrond Myklebust 	trace_nfs4_layouterror(inode, &data->args.errors[0].stateid,
934638037b1STrond Myklebust 			       task->tk_status);
9353eb86093STrond Myklebust }
9363eb86093STrond Myklebust 
9373eb86093STrond Myklebust static void
9383eb86093STrond Myklebust nfs42_layouterror_release(void *calldata)
9393eb86093STrond Myklebust {
9403eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
9413eb86093STrond Myklebust 
9423eb86093STrond Myklebust 	nfs42_free_layouterror_data(data);
9433eb86093STrond Myklebust }
9443eb86093STrond Myklebust 
9453eb86093STrond Myklebust static const struct rpc_call_ops nfs42_layouterror_ops = {
9463eb86093STrond Myklebust 	.rpc_call_prepare = nfs42_layouterror_prepare,
9473eb86093STrond Myklebust 	.rpc_call_done = nfs42_layouterror_done,
9483eb86093STrond Myklebust 	.rpc_release = nfs42_layouterror_release,
9493eb86093STrond Myklebust };
9503eb86093STrond Myklebust 
9513eb86093STrond Myklebust int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
9523eb86093STrond Myklebust 		const struct nfs42_layout_error *errors, size_t n)
9533eb86093STrond Myklebust {
9543eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
9553eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
9563eb86093STrond Myklebust 	struct rpc_task *task;
9573eb86093STrond Myklebust 	struct rpc_message msg = {
9583eb86093STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTERROR],
9593eb86093STrond Myklebust 	};
9603eb86093STrond Myklebust 	struct rpc_task_setup task_setup = {
9613eb86093STrond Myklebust 		.rpc_message = &msg,
9623eb86093STrond Myklebust 		.callback_ops = &nfs42_layouterror_ops,
9633eb86093STrond Myklebust 		.flags = RPC_TASK_ASYNC,
9643eb86093STrond Myklebust 	};
9653eb86093STrond Myklebust 	unsigned int i;
9663eb86093STrond Myklebust 
9673eb86093STrond Myklebust 	if (!nfs_server_capable(inode, NFS_CAP_LAYOUTERROR))
9683eb86093STrond Myklebust 		return -EOPNOTSUPP;
9693eb86093STrond Myklebust 	if (n > NFS42_LAYOUTERROR_MAX)
9703eb86093STrond Myklebust 		return -EINVAL;
9713eb86093STrond Myklebust 	data = nfs42_alloc_layouterror_data(lseg, GFP_NOFS);
9723eb86093STrond Myklebust 	if (!data)
9733eb86093STrond Myklebust 		return -ENOMEM;
9743eb86093STrond Myklebust 	for (i = 0; i < n; i++) {
9753eb86093STrond Myklebust 		data->args.errors[i] = errors[i];
9763eb86093STrond Myklebust 		data->args.num_errors++;
9773eb86093STrond Myklebust 		data->res.num_errors++;
9783eb86093STrond Myklebust 	}
9793eb86093STrond Myklebust 	msg.rpc_argp = &data->args;
9803eb86093STrond Myklebust 	msg.rpc_resp = &data->res;
9813eb86093STrond Myklebust 	task_setup.callback_data = data;
9823eb86093STrond Myklebust 	task_setup.rpc_client = NFS_SERVER(inode)->client;
9833eb86093STrond Myklebust 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
9843eb86093STrond Myklebust 	task = rpc_run_task(&task_setup);
9853eb86093STrond Myklebust 	if (IS_ERR(task))
9863eb86093STrond Myklebust 		return PTR_ERR(task);
9873eb86093STrond Myklebust 	rpc_put_task(task);
9883eb86093STrond Myklebust 	return 0;
9893eb86093STrond Myklebust }
9903eb86093STrond Myklebust EXPORT_SYMBOL_GPL(nfs42_proc_layouterror);
9913eb86093STrond Myklebust 
992e5341f3aSPeng Tao static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
9934bdf87ebSChristoph Hellwig 		struct file *dst_f, struct nfs_lock_context *src_lock,
9944bdf87ebSChristoph Hellwig 		struct nfs_lock_context *dst_lock, loff_t src_offset,
995e5341f3aSPeng Tao 		loff_t dst_offset, loff_t count)
996e5341f3aSPeng Tao {
997e5341f3aSPeng Tao 	struct inode *src_inode = file_inode(src_f);
998e5341f3aSPeng Tao 	struct inode *dst_inode = file_inode(dst_f);
999e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(dst_inode);
1000e5341f3aSPeng Tao 	struct nfs42_clone_args args = {
1001e5341f3aSPeng Tao 		.src_fh = NFS_FH(src_inode),
1002e5341f3aSPeng Tao 		.dst_fh = NFS_FH(dst_inode),
1003e5341f3aSPeng Tao 		.src_offset = src_offset,
1004e5341f3aSPeng Tao 		.dst_offset = dst_offset,
10059494b2ceSChristoph Hellwig 		.count = count,
1006e5341f3aSPeng Tao 		.dst_bitmask = server->cache_consistency_bitmask,
1007e5341f3aSPeng Tao 	};
1008e5341f3aSPeng Tao 	struct nfs42_clone_res res = {
1009e5341f3aSPeng Tao 		.server	= server,
1010e5341f3aSPeng Tao 	};
1011e5341f3aSPeng Tao 	int status;
1012e5341f3aSPeng Tao 
1013e5341f3aSPeng Tao 	msg->rpc_argp = &args;
1014e5341f3aSPeng Tao 	msg->rpc_resp = &res;
1015e5341f3aSPeng Tao 
10164bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context,
10174bdf87ebSChristoph Hellwig 			src_lock, FMODE_READ);
1018d826e5b8SOlga Kornievskaia 	if (status) {
1019d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
1020d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
1021e5341f3aSPeng Tao 		return status;
1022d826e5b8SOlga Kornievskaia 	}
10234bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
10244bdf87ebSChristoph Hellwig 			dst_lock, FMODE_WRITE);
1025d826e5b8SOlga Kornievskaia 	if (status) {
1026d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
1027d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
1028e5341f3aSPeng Tao 		return status;
1029d826e5b8SOlga Kornievskaia 	}
1030e5341f3aSPeng Tao 
1031e5341f3aSPeng Tao 	res.dst_fattr = nfs_alloc_fattr();
1032e5341f3aSPeng Tao 	if (!res.dst_fattr)
1033e5341f3aSPeng Tao 		return -ENOMEM;
1034e5341f3aSPeng Tao 
1035e5341f3aSPeng Tao 	status = nfs4_call_sync(server->client, server, msg,
1036e5341f3aSPeng Tao 				&args.seq_args, &res.seq_res, 0);
1037e5341f3aSPeng Tao 	if (status == 0)
1038e5341f3aSPeng Tao 		status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
1039e5341f3aSPeng Tao 
1040e5341f3aSPeng Tao 	kfree(res.dst_fattr);
1041e5341f3aSPeng Tao 	return status;
1042e5341f3aSPeng Tao }
1043e5341f3aSPeng Tao 
1044e5341f3aSPeng Tao int nfs42_proc_clone(struct file *src_f, struct file *dst_f,
1045e5341f3aSPeng Tao 		     loff_t src_offset, loff_t dst_offset, loff_t count)
1046e5341f3aSPeng Tao {
1047e5341f3aSPeng Tao 	struct rpc_message msg = {
1048e5341f3aSPeng Tao 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE],
1049e5341f3aSPeng Tao 	};
1050e5341f3aSPeng Tao 	struct inode *inode = file_inode(src_f);
1051e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(file_inode(src_f));
10524bdf87ebSChristoph Hellwig 	struct nfs_lock_context *src_lock;
10534bdf87ebSChristoph Hellwig 	struct nfs_lock_context *dst_lock;
10544bdf87ebSChristoph Hellwig 	struct nfs4_exception src_exception = { };
10554bdf87ebSChristoph Hellwig 	struct nfs4_exception dst_exception = { };
10564bdf87ebSChristoph Hellwig 	int err, err2;
1057e5341f3aSPeng Tao 
1058e5341f3aSPeng Tao 	if (!nfs_server_capable(inode, NFS_CAP_CLONE))
1059e5341f3aSPeng Tao 		return -EOPNOTSUPP;
1060e5341f3aSPeng Tao 
10614bdf87ebSChristoph Hellwig 	src_lock = nfs_get_lock_context(nfs_file_open_context(src_f));
10624bdf87ebSChristoph Hellwig 	if (IS_ERR(src_lock))
10634bdf87ebSChristoph Hellwig 		return PTR_ERR(src_lock);
10644bdf87ebSChristoph Hellwig 
10654bdf87ebSChristoph Hellwig 	src_exception.inode = file_inode(src_f);
10664bdf87ebSChristoph Hellwig 	src_exception.state = src_lock->open_context->state;
10674bdf87ebSChristoph Hellwig 
10684bdf87ebSChristoph Hellwig 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f));
10694bdf87ebSChristoph Hellwig 	if (IS_ERR(dst_lock)) {
10704bdf87ebSChristoph Hellwig 		err = PTR_ERR(dst_lock);
10714bdf87ebSChristoph Hellwig 		goto out_put_src_lock;
10724bdf87ebSChristoph Hellwig 	}
10734bdf87ebSChristoph Hellwig 
10744bdf87ebSChristoph Hellwig 	dst_exception.inode = file_inode(dst_f);
10754bdf87ebSChristoph Hellwig 	dst_exception.state = dst_lock->open_context->state;
10764bdf87ebSChristoph Hellwig 
1077e5341f3aSPeng Tao 	do {
10784bdf87ebSChristoph Hellwig 		err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock,
10794bdf87ebSChristoph Hellwig 					src_offset, dst_offset, count);
1080e5341f3aSPeng Tao 		if (err == -ENOTSUPP || err == -EOPNOTSUPP) {
1081e5341f3aSPeng Tao 			NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE;
10824bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
10834bdf87ebSChristoph Hellwig 			break;
1084e5341f3aSPeng Tao 		}
1085e5341f3aSPeng Tao 
10864bdf87ebSChristoph Hellwig 		err2 = nfs4_handle_exception(server, err, &src_exception);
10874bdf87ebSChristoph Hellwig 		err = nfs4_handle_exception(server, err, &dst_exception);
10884bdf87ebSChristoph Hellwig 		if (!err)
10894bdf87ebSChristoph Hellwig 			err = err2;
10904bdf87ebSChristoph Hellwig 	} while (src_exception.retry || dst_exception.retry);
10914bdf87ebSChristoph Hellwig 
10924bdf87ebSChristoph Hellwig 	nfs_put_lock_context(dst_lock);
10934bdf87ebSChristoph Hellwig out_put_src_lock:
10944bdf87ebSChristoph Hellwig 	nfs_put_lock_context(src_lock);
1095e5341f3aSPeng Tao 	return err;
1096e5341f3aSPeng Tao }
1097c10a7514SFrank van der Linden 
1098c10a7514SFrank van der Linden #define NFS4XATTR_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
1099c10a7514SFrank van der Linden 
1100c10a7514SFrank van der Linden static int _nfs42_proc_removexattr(struct inode *inode, const char *name)
1101c10a7514SFrank van der Linden {
1102c10a7514SFrank van der Linden 	struct nfs_server *server = NFS_SERVER(inode);
1103c10a7514SFrank van der Linden 	struct nfs42_removexattrargs args = {
1104c10a7514SFrank van der Linden 		.fh = NFS_FH(inode),
1105c10a7514SFrank van der Linden 		.xattr_name = name,
1106c10a7514SFrank van der Linden 	};
1107c10a7514SFrank van der Linden 	struct nfs42_removexattrres res;
1108c10a7514SFrank van der Linden 	struct rpc_message msg = {
1109c10a7514SFrank van der Linden 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVEXATTR],
1110c10a7514SFrank van der Linden 		.rpc_argp = &args,
1111c10a7514SFrank van der Linden 		.rpc_resp = &res,
1112c10a7514SFrank van der Linden 	};
1113c10a7514SFrank van der Linden 	int ret;
1114c10a7514SFrank van der Linden 	unsigned long timestamp = jiffies;
1115c10a7514SFrank van der Linden 
1116c10a7514SFrank van der Linden 	ret = nfs4_call_sync(server->client, server, &msg, &args.seq_args,
1117c10a7514SFrank van der Linden 	    &res.seq_res, 1);
1118c10a7514SFrank van der Linden 	if (!ret)
1119c10a7514SFrank van der Linden 		nfs4_update_changeattr(inode, &res.cinfo, timestamp, 0);
1120c10a7514SFrank van der Linden 
1121c10a7514SFrank van der Linden 	return ret;
1122c10a7514SFrank van der Linden }
1123c10a7514SFrank van der Linden 
1124c10a7514SFrank van der Linden static int _nfs42_proc_setxattr(struct inode *inode, const char *name,
1125c10a7514SFrank van der Linden 				const void *buf, size_t buflen, int flags)
1126c10a7514SFrank van der Linden {
1127c10a7514SFrank van der Linden 	struct nfs_server *server = NFS_SERVER(inode);
1128c10a7514SFrank van der Linden 	struct page *pages[NFS4XATTR_MAXPAGES];
1129c10a7514SFrank van der Linden 	struct nfs42_setxattrargs arg = {
1130c10a7514SFrank van der Linden 		.fh		= NFS_FH(inode),
1131c10a7514SFrank van der Linden 		.xattr_pages	= pages,
1132c10a7514SFrank van der Linden 		.xattr_len	= buflen,
1133c10a7514SFrank van der Linden 		.xattr_name	= name,
1134c10a7514SFrank van der Linden 		.xattr_flags	= flags,
1135c10a7514SFrank van der Linden 	};
1136c10a7514SFrank van der Linden 	struct nfs42_setxattrres res;
1137c10a7514SFrank van der Linden 	struct rpc_message msg = {
1138c10a7514SFrank van der Linden 		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_SETXATTR],
1139c10a7514SFrank van der Linden 		.rpc_argp	= &arg,
1140c10a7514SFrank van der Linden 		.rpc_resp	= &res,
1141c10a7514SFrank van der Linden 	};
1142c10a7514SFrank van der Linden 	int ret, np;
1143c10a7514SFrank van der Linden 	unsigned long timestamp = jiffies;
1144c10a7514SFrank van der Linden 
1145c10a7514SFrank van der Linden 	if (buflen > server->sxasize)
1146c10a7514SFrank van der Linden 		return -ERANGE;
1147c10a7514SFrank van der Linden 
1148c10a7514SFrank van der Linden 	if (buflen > 0) {
1149c10a7514SFrank van der Linden 		np = nfs4_buf_to_pages_noslab(buf, buflen, arg.xattr_pages);
1150c10a7514SFrank van der Linden 		if (np < 0)
1151c10a7514SFrank van der Linden 			return np;
1152c10a7514SFrank van der Linden 	} else
1153c10a7514SFrank van der Linden 		np = 0;
1154c10a7514SFrank van der Linden 
1155c10a7514SFrank van der Linden 	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
1156c10a7514SFrank van der Linden 	    &res.seq_res, 1);
1157c10a7514SFrank van der Linden 
1158c10a7514SFrank van der Linden 	for (; np > 0; np--)
1159c10a7514SFrank van der Linden 		put_page(pages[np - 1]);
1160c10a7514SFrank van der Linden 
1161c10a7514SFrank van der Linden 	if (!ret)
1162c10a7514SFrank van der Linden 		nfs4_update_changeattr(inode, &res.cinfo, timestamp, 0);
1163c10a7514SFrank van der Linden 
1164c10a7514SFrank van der Linden 	return ret;
1165c10a7514SFrank van der Linden }
1166c10a7514SFrank van der Linden 
1167c10a7514SFrank van der Linden static ssize_t _nfs42_proc_getxattr(struct inode *inode, const char *name,
1168c10a7514SFrank van der Linden 				void *buf, size_t buflen)
1169c10a7514SFrank van der Linden {
1170c10a7514SFrank van der Linden 	struct nfs_server *server = NFS_SERVER(inode);
1171c10a7514SFrank van der Linden 	struct page *pages[NFS4XATTR_MAXPAGES] = {};
1172c10a7514SFrank van der Linden 	struct nfs42_getxattrargs arg = {
1173c10a7514SFrank van der Linden 		.fh		= NFS_FH(inode),
1174c10a7514SFrank van der Linden 		.xattr_pages	= pages,
1175c10a7514SFrank van der Linden 		.xattr_len	= buflen,
1176c10a7514SFrank van der Linden 		.xattr_name	= name,
1177c10a7514SFrank van der Linden 	};
1178c10a7514SFrank van der Linden 	struct nfs42_getxattrres res;
1179c10a7514SFrank van der Linden 	struct rpc_message msg = {
1180c10a7514SFrank van der Linden 		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_GETXATTR],
1181c10a7514SFrank van der Linden 		.rpc_argp	= &arg,
1182c10a7514SFrank van der Linden 		.rpc_resp	= &res,
1183c10a7514SFrank van der Linden 	};
1184c10a7514SFrank van der Linden 	int ret, np;
1185c10a7514SFrank van der Linden 
1186c10a7514SFrank van der Linden 	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
1187c10a7514SFrank van der Linden 	    &res.seq_res, 0);
1188c10a7514SFrank van der Linden 	if (ret < 0)
1189c10a7514SFrank van der Linden 		return ret;
1190c10a7514SFrank van der Linden 
119195ad37f9SFrank van der Linden 	/*
119295ad37f9SFrank van der Linden 	 * Normally, the caching is done one layer up, but for successful
119395ad37f9SFrank van der Linden 	 * RPCS, always cache the result here, even if the caller was
119495ad37f9SFrank van der Linden 	 * just querying the length, or if the reply was too big for
119595ad37f9SFrank van der Linden 	 * the caller. This avoids a second RPC in the case of the
119695ad37f9SFrank van der Linden 	 * common query-alloc-retrieve cycle for xattrs.
119795ad37f9SFrank van der Linden 	 *
119895ad37f9SFrank van der Linden 	 * Note that xattr_len is always capped to XATTR_SIZE_MAX.
119995ad37f9SFrank van der Linden 	 */
120095ad37f9SFrank van der Linden 
120195ad37f9SFrank van der Linden 	nfs4_xattr_cache_add(inode, name, NULL, pages, res.xattr_len);
120295ad37f9SFrank van der Linden 
1203c10a7514SFrank van der Linden 	if (buflen) {
1204c10a7514SFrank van der Linden 		if (res.xattr_len > buflen)
1205c10a7514SFrank van der Linden 			return -ERANGE;
1206c10a7514SFrank van der Linden 		_copy_from_pages(buf, pages, 0, res.xattr_len);
1207c10a7514SFrank van der Linden 	}
1208c10a7514SFrank van der Linden 
1209c10a7514SFrank van der Linden 	np = DIV_ROUND_UP(res.xattr_len, PAGE_SIZE);
1210c10a7514SFrank van der Linden 	while (--np >= 0)
1211c10a7514SFrank van der Linden 		__free_page(pages[np]);
1212c10a7514SFrank van der Linden 
1213c10a7514SFrank van der Linden 	return res.xattr_len;
1214c10a7514SFrank van der Linden }
1215c10a7514SFrank van der Linden 
1216c10a7514SFrank van der Linden static ssize_t _nfs42_proc_listxattrs(struct inode *inode, void *buf,
1217c10a7514SFrank van der Linden 				 size_t buflen, u64 *cookiep, bool *eofp)
1218c10a7514SFrank van der Linden {
1219c10a7514SFrank van der Linden 	struct nfs_server *server = NFS_SERVER(inode);
1220c10a7514SFrank van der Linden 	struct page **pages;
1221c10a7514SFrank van der Linden 	struct nfs42_listxattrsargs arg = {
1222c10a7514SFrank van der Linden 		.fh		= NFS_FH(inode),
1223c10a7514SFrank van der Linden 		.cookie		= *cookiep,
1224c10a7514SFrank van der Linden 	};
1225c10a7514SFrank van der Linden 	struct nfs42_listxattrsres res = {
1226c10a7514SFrank van der Linden 		.eof = false,
1227c10a7514SFrank van der Linden 		.xattr_buf = buf,
1228c10a7514SFrank van der Linden 		.xattr_len = buflen,
1229c10a7514SFrank van der Linden 	};
1230c10a7514SFrank van der Linden 	struct rpc_message msg = {
1231c10a7514SFrank van der Linden 		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_LISTXATTRS],
1232c10a7514SFrank van der Linden 		.rpc_argp	= &arg,
1233c10a7514SFrank van der Linden 		.rpc_resp	= &res,
1234c10a7514SFrank van der Linden 	};
1235c10a7514SFrank van der Linden 	u32 xdrlen;
1236c10a7514SFrank van der Linden 	int ret, np;
1237c10a7514SFrank van der Linden 
1238c10a7514SFrank van der Linden 
1239c10a7514SFrank van der Linden 	res.scratch = alloc_page(GFP_KERNEL);
1240c10a7514SFrank van der Linden 	if (!res.scratch)
1241c10a7514SFrank van der Linden 		return -ENOMEM;
1242c10a7514SFrank van der Linden 
1243c10a7514SFrank van der Linden 	xdrlen = nfs42_listxattr_xdrsize(buflen);
1244c10a7514SFrank van der Linden 	if (xdrlen > server->lxasize)
1245c10a7514SFrank van der Linden 		xdrlen = server->lxasize;
1246c10a7514SFrank van der Linden 	np = xdrlen / PAGE_SIZE + 1;
1247c10a7514SFrank van der Linden 
1248c10a7514SFrank van der Linden 	pages = kcalloc(np, sizeof(struct page *), GFP_KERNEL);
1249c10a7514SFrank van der Linden 	if (pages == NULL) {
1250c10a7514SFrank van der Linden 		__free_page(res.scratch);
1251c10a7514SFrank van der Linden 		return -ENOMEM;
1252c10a7514SFrank van der Linden 	}
1253c10a7514SFrank van der Linden 
1254c10a7514SFrank van der Linden 	arg.xattr_pages = pages;
1255c10a7514SFrank van der Linden 	arg.count = xdrlen;
1256c10a7514SFrank van der Linden 
1257c10a7514SFrank van der Linden 	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
1258c10a7514SFrank van der Linden 	    &res.seq_res, 0);
1259c10a7514SFrank van der Linden 
1260c10a7514SFrank van der Linden 	if (ret >= 0) {
1261c10a7514SFrank van der Linden 		ret = res.copied;
1262c10a7514SFrank van der Linden 		*cookiep = res.cookie;
1263c10a7514SFrank van der Linden 		*eofp = res.eof;
1264c10a7514SFrank van der Linden 	}
1265c10a7514SFrank van der Linden 
1266c10a7514SFrank van der Linden 	while (--np >= 0) {
1267c10a7514SFrank van der Linden 		if (pages[np])
1268c10a7514SFrank van der Linden 			__free_page(pages[np]);
1269c10a7514SFrank van der Linden 	}
1270c10a7514SFrank van der Linden 
1271c10a7514SFrank van der Linden 	__free_page(res.scratch);
1272c10a7514SFrank van der Linden 	kfree(pages);
1273c10a7514SFrank van der Linden 
1274c10a7514SFrank van der Linden 	return ret;
1275c10a7514SFrank van der Linden 
1276c10a7514SFrank van der Linden }
1277c10a7514SFrank van der Linden 
1278c10a7514SFrank van der Linden ssize_t nfs42_proc_getxattr(struct inode *inode, const char *name,
1279c10a7514SFrank van der Linden 			      void *buf, size_t buflen)
1280c10a7514SFrank van der Linden {
1281c10a7514SFrank van der Linden 	struct nfs4_exception exception = { };
1282c10a7514SFrank van der Linden 	ssize_t err;
1283c10a7514SFrank van der Linden 
1284c10a7514SFrank van der Linden 	do {
1285c10a7514SFrank van der Linden 		err = _nfs42_proc_getxattr(inode, name, buf, buflen);
1286c10a7514SFrank van der Linden 		if (err >= 0)
1287c10a7514SFrank van der Linden 			break;
1288c10a7514SFrank van der Linden 		err = nfs4_handle_exception(NFS_SERVER(inode), err,
1289c10a7514SFrank van der Linden 				&exception);
1290c10a7514SFrank van der Linden 	} while (exception.retry);
1291c10a7514SFrank van der Linden 
1292c10a7514SFrank van der Linden 	return err;
1293c10a7514SFrank van der Linden }
1294c10a7514SFrank van der Linden 
1295c10a7514SFrank van der Linden int nfs42_proc_setxattr(struct inode *inode, const char *name,
1296c10a7514SFrank van der Linden 			      const void *buf, size_t buflen, int flags)
1297c10a7514SFrank van der Linden {
1298c10a7514SFrank van der Linden 	struct nfs4_exception exception = { };
1299c10a7514SFrank van der Linden 	int err;
1300c10a7514SFrank van der Linden 
1301c10a7514SFrank van der Linden 	do {
1302c10a7514SFrank van der Linden 		err = _nfs42_proc_setxattr(inode, name, buf, buflen, flags);
1303c10a7514SFrank van der Linden 		if (!err)
1304c10a7514SFrank van der Linden 			break;
1305c10a7514SFrank van der Linden 		err = nfs4_handle_exception(NFS_SERVER(inode), err,
1306c10a7514SFrank van der Linden 				&exception);
1307c10a7514SFrank van der Linden 	} while (exception.retry);
1308c10a7514SFrank van der Linden 
1309c10a7514SFrank van der Linden 	return err;
1310c10a7514SFrank van der Linden }
1311c10a7514SFrank van der Linden 
1312c10a7514SFrank van der Linden ssize_t nfs42_proc_listxattrs(struct inode *inode, void *buf,
1313c10a7514SFrank van der Linden 			      size_t buflen, u64 *cookiep, bool *eofp)
1314c10a7514SFrank van der Linden {
1315c10a7514SFrank van der Linden 	struct nfs4_exception exception = { };
1316c10a7514SFrank van der Linden 	ssize_t err;
1317c10a7514SFrank van der Linden 
1318c10a7514SFrank van der Linden 	do {
1319c10a7514SFrank van der Linden 		err = _nfs42_proc_listxattrs(inode, buf, buflen,
1320c10a7514SFrank van der Linden 		    cookiep, eofp);
1321c10a7514SFrank van der Linden 		if (err >= 0)
1322c10a7514SFrank van der Linden 			break;
1323c10a7514SFrank van der Linden 		err = nfs4_handle_exception(NFS_SERVER(inode), err,
1324c10a7514SFrank van der Linden 				&exception);
1325c10a7514SFrank van der Linden 	} while (exception.retry);
1326c10a7514SFrank van der Linden 
1327c10a7514SFrank van der Linden 	return err;
1328c10a7514SFrank van der Linden }
1329c10a7514SFrank van der Linden 
1330c10a7514SFrank van der Linden int nfs42_proc_removexattr(struct inode *inode, const char *name)
1331c10a7514SFrank van der Linden {
1332c10a7514SFrank van der Linden 	struct nfs4_exception exception = { };
1333c10a7514SFrank van der Linden 	int err;
1334c10a7514SFrank van der Linden 
1335c10a7514SFrank van der Linden 	do {
1336c10a7514SFrank van der Linden 		err = _nfs42_proc_removexattr(inode, name);
1337c10a7514SFrank van der Linden 		if (!err)
1338c10a7514SFrank van der Linden 			break;
1339c10a7514SFrank van der Linden 		err = nfs4_handle_exception(NFS_SERVER(inode), err,
1340c10a7514SFrank van der Linden 				&exception);
1341c10a7514SFrank van der Linden 	} while (exception.retry);
1342c10a7514SFrank van der Linden 
1343c10a7514SFrank van der Linden 	return err;
1344c10a7514SFrank van der Linden }
1345