xref: /openbmc/linux/fs/nfs/nfs42proc.c (revision f588d72b)
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 
nfs42_set_netaddr(struct file * filep,struct nfs42_netaddr * naddr)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 
_nfs42_proc_fallocate(struct rpc_message * msg,struct file * filep,struct nfs_lock_context * lock,loff_t offset,loff_t len)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);
4934bf20ceSTrond Myklebust 	u32 bitmask[NFS_BITMASK_SZ];
50f4ac1674SAnna Schumaker 	struct nfs42_falloc_args args = {
51f4ac1674SAnna Schumaker 		.falloc_fh	= NFS_FH(inode),
52f4ac1674SAnna Schumaker 		.falloc_offset	= offset,
53f4ac1674SAnna Schumaker 		.falloc_length	= len,
54e99812e1STrond Myklebust 		.falloc_bitmask	= bitmask,
55f4ac1674SAnna Schumaker 	};
569a51940bSAnna Schumaker 	struct nfs42_falloc_res res = {
579a51940bSAnna Schumaker 		.falloc_server	= server,
589a51940bSAnna Schumaker 	};
59f4ac1674SAnna Schumaker 	int status;
60f4ac1674SAnna Schumaker 
61f4ac1674SAnna Schumaker 	msg->rpc_argp = &args;
62f4ac1674SAnna Schumaker 	msg->rpc_resp = &res;
63f4ac1674SAnna Schumaker 
644bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.falloc_stateid, lock->open_context,
654bdf87ebSChristoph Hellwig 			lock, FMODE_WRITE);
66d826e5b8SOlga Kornievskaia 	if (status) {
67d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
68d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
69f4ac1674SAnna Schumaker 		return status;
70d826e5b8SOlga Kornievskaia 	}
71f4ac1674SAnna Schumaker 
7234bf20ceSTrond Myklebust 	nfs4_bitmask_set(bitmask, server->cache_consistency_bitmask, inode,
7334bf20ceSTrond Myklebust 			 NFS_INO_INVALID_BLOCKS);
74e99812e1STrond Myklebust 
759a51940bSAnna Schumaker 	res.falloc_fattr = nfs_alloc_fattr();
769a51940bSAnna Schumaker 	if (!res.falloc_fattr)
779a51940bSAnna Schumaker 		return -ENOMEM;
789a51940bSAnna Schumaker 
799a51940bSAnna Schumaker 	status = nfs4_call_sync(server->client, server, msg,
80f4ac1674SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
81d7a51186SAnna Schumaker 	if (status == 0) {
82d7a51186SAnna Schumaker 		if (nfs_should_remove_suid(inode)) {
83d7a51186SAnna Schumaker 			spin_lock(&inode->i_lock);
84*f588d72bSDai Ngo 			nfs_set_cache_invalid(inode,
85*f588d72bSDai Ngo 				NFS_INO_REVAL_FORCED | NFS_INO_INVALID_MODE);
86d7a51186SAnna Schumaker 			spin_unlock(&inode->i_lock);
87d7a51186SAnna Schumaker 		}
88e99812e1STrond Myklebust 		status = nfs_post_op_update_inode_force_wcc(inode,
89e99812e1STrond Myklebust 							    res.falloc_fattr);
90d7a51186SAnna Schumaker 	}
9140a82417SOlga Kornievskaia 	if (msg->rpc_proc == &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE])
9240a82417SOlga Kornievskaia 		trace_nfs4_fallocate(inode, &args, status);
9340a82417SOlga Kornievskaia 	else
9440a82417SOlga Kornievskaia 		trace_nfs4_deallocate(inode, &args, status);
959a51940bSAnna Schumaker 	kfree(res.falloc_fattr);
969a51940bSAnna Schumaker 	return status;
97f4ac1674SAnna Schumaker }
98f4ac1674SAnna Schumaker 
nfs42_proc_fallocate(struct rpc_message * msg,struct file * filep,loff_t offset,loff_t len)99f4ac1674SAnna Schumaker static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
100f4ac1674SAnna Schumaker 				loff_t offset, loff_t len)
101f4ac1674SAnna Schumaker {
10299f23783STrond Myklebust 	struct inode *inode = file_inode(filep);
10399f23783STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
104f4ac1674SAnna Schumaker 	struct nfs4_exception exception = { };
1054bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
106f4ac1674SAnna Schumaker 	int err;
107f4ac1674SAnna Schumaker 
1084bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
1094bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
1104bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
1114bdf87ebSChristoph Hellwig 
11299f23783STrond Myklebust 	exception.inode = inode;
1134bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
1144bdf87ebSChristoph Hellwig 
11599f23783STrond Myklebust 	err = nfs_sync_inode(inode);
11699f23783STrond Myklebust 	if (err)
11799f23783STrond Myklebust 		goto out;
11899f23783STrond Myklebust 
119f4ac1674SAnna Schumaker 	do {
1204bdf87ebSChristoph Hellwig 		err = _nfs42_proc_fallocate(msg, filep, lock, offset, len);
1214bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
1224bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
1234bdf87ebSChristoph Hellwig 			break;
1244bdf87ebSChristoph Hellwig 		}
125f4ac1674SAnna Schumaker 		err = nfs4_handle_exception(server, err, &exception);
126f4ac1674SAnna Schumaker 	} while (exception.retry);
12799f23783STrond Myklebust out:
1284bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
129f4ac1674SAnna Schumaker 	return err;
130f4ac1674SAnna Schumaker }
131f4ac1674SAnna Schumaker 
nfs42_proc_allocate(struct file * filep,loff_t offset,loff_t len)132f4ac1674SAnna Schumaker int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
133f4ac1674SAnna Schumaker {
134f4ac1674SAnna Schumaker 	struct rpc_message msg = {
135f4ac1674SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE],
136f4ac1674SAnna Schumaker 	};
137f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
138f4ac1674SAnna Schumaker 	int err;
139f4ac1674SAnna Schumaker 
140f4ac1674SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE))
141f4ac1674SAnna Schumaker 		return -EOPNOTSUPP;
142f4ac1674SAnna Schumaker 
1435955102cSAl Viro 	inode_lock(inode);
144f830f7ddSAnna Schumaker 
145f4ac1674SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
146f4ac1674SAnna Schumaker 	if (err == -EOPNOTSUPP)
147f4ac1674SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE;
148f830f7ddSAnna Schumaker 
1495955102cSAl Viro 	inode_unlock(inode);
150f4ac1674SAnna Schumaker 	return err;
151f4ac1674SAnna Schumaker }
152f4ac1674SAnna Schumaker 
nfs42_proc_deallocate(struct file * filep,loff_t offset,loff_t len)153624bd5b7SAnna Schumaker int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
154624bd5b7SAnna Schumaker {
155624bd5b7SAnna Schumaker 	struct rpc_message msg = {
156624bd5b7SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE],
157624bd5b7SAnna Schumaker 	};
158624bd5b7SAnna Schumaker 	struct inode *inode = file_inode(filep);
159624bd5b7SAnna Schumaker 	int err;
160624bd5b7SAnna Schumaker 
161624bd5b7SAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE))
162624bd5b7SAnna Schumaker 		return -EOPNOTSUPP;
163624bd5b7SAnna Schumaker 
1645955102cSAl Viro 	inode_lock(inode);
165f830f7ddSAnna Schumaker 
166624bd5b7SAnna Schumaker 	err = nfs42_proc_fallocate(&msg, filep, offset, len);
1679a51940bSAnna Schumaker 	if (err == 0)
1689a51940bSAnna Schumaker 		truncate_pagecache_range(inode, offset, (offset + len) -1);
169624bd5b7SAnna Schumaker 	if (err == -EOPNOTSUPP)
170624bd5b7SAnna Schumaker 		NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE;
17199f23783STrond Myklebust 
1725955102cSAl Viro 	inode_unlock(inode);
173624bd5b7SAnna Schumaker 	return err;
174624bd5b7SAnna Schumaker }
175624bd5b7SAnna Schumaker 
handle_async_copy(struct nfs42_copy_res * res,struct nfs_server * dst_server,struct nfs_server * src_server,struct file * src,struct file * dst,nfs4_stateid * src_stateid,bool * restart)17662164f31SOlga Kornievskaia static int handle_async_copy(struct nfs42_copy_res *res,
1770e65a32cSOlga Kornievskaia 			     struct nfs_server *dst_server,
1780e65a32cSOlga Kornievskaia 			     struct nfs_server *src_server,
17962164f31SOlga Kornievskaia 			     struct file *src,
18062164f31SOlga Kornievskaia 			     struct file *dst,
1810e65a32cSOlga Kornievskaia 			     nfs4_stateid *src_stateid,
1820e65a32cSOlga Kornievskaia 			     bool *restart)
18362164f31SOlga Kornievskaia {
1843de24f3dSJakob Koschel 	struct nfs4_copy_state *copy, *tmp_copy = NULL, *iter;
18562164f31SOlga Kornievskaia 	int status = NFS4_OK;
1860e65a32cSOlga Kornievskaia 	struct nfs_open_context *dst_ctx = nfs_file_open_context(dst);
1870e65a32cSOlga Kornievskaia 	struct nfs_open_context *src_ctx = nfs_file_open_context(src);
188bc0c9079SOlga Kornievskaia 
1894fb547beSTrond Myklebust 	copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_KERNEL);
19099f2c555SOlga Kornievskaia 	if (!copy)
19199f2c555SOlga Kornievskaia 		return -ENOMEM;
19299f2c555SOlga Kornievskaia 
1930e65a32cSOlga Kornievskaia 	spin_lock(&dst_server->nfs_client->cl_lock);
1943de24f3dSJakob Koschel 	list_for_each_entry(iter,
1950e65a32cSOlga Kornievskaia 				&dst_server->nfs_client->pending_cb_stateids,
196bc0c9079SOlga Kornievskaia 				copies) {
1973de24f3dSJakob Koschel 		if (memcmp(&res->write_res.stateid, &iter->stateid,
198bc0c9079SOlga Kornievskaia 				NFS4_STATEID_SIZE))
199bc0c9079SOlga Kornievskaia 			continue;
2003de24f3dSJakob Koschel 		tmp_copy = iter;
2013de24f3dSJakob Koschel 		list_del(&iter->copies);
202bc0c9079SOlga Kornievskaia 		break;
203bc0c9079SOlga Kornievskaia 	}
2043de24f3dSJakob Koschel 	if (tmp_copy) {
2050e65a32cSOlga Kornievskaia 		spin_unlock(&dst_server->nfs_client->cl_lock);
20699f2c555SOlga Kornievskaia 		kfree(copy);
20799f2c555SOlga Kornievskaia 		copy = tmp_copy;
208bc0c9079SOlga Kornievskaia 		goto out;
209bc0c9079SOlga Kornievskaia 	}
21062164f31SOlga Kornievskaia 
21162164f31SOlga Kornievskaia 	memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
21262164f31SOlga Kornievskaia 	init_completion(&copy->completion);
2130e65a32cSOlga Kornievskaia 	copy->parent_dst_state = dst_ctx->state;
2140e65a32cSOlga Kornievskaia 	copy->parent_src_state = src_ctx->state;
21562164f31SOlga Kornievskaia 
2160e65a32cSOlga Kornievskaia 	list_add_tail(&copy->copies, &dst_server->ss_copies);
2170e65a32cSOlga Kornievskaia 	spin_unlock(&dst_server->nfs_client->cl_lock);
2180e65a32cSOlga Kornievskaia 
2190e65a32cSOlga Kornievskaia 	if (dst_server != src_server) {
2200e65a32cSOlga Kornievskaia 		spin_lock(&src_server->nfs_client->cl_lock);
2210e65a32cSOlga Kornievskaia 		list_add_tail(&copy->src_copies, &src_server->ss_copies);
2220e65a32cSOlga Kornievskaia 		spin_unlock(&src_server->nfs_client->cl_lock);
2230e65a32cSOlga Kornievskaia 	}
22462164f31SOlga Kornievskaia 
225c975c209SOlga Kornievskaia 	status = wait_for_completion_interruptible(&copy->completion);
2260e65a32cSOlga Kornievskaia 	spin_lock(&dst_server->nfs_client->cl_lock);
22762164f31SOlga Kornievskaia 	list_del_init(&copy->copies);
2280e65a32cSOlga Kornievskaia 	spin_unlock(&dst_server->nfs_client->cl_lock);
2290e65a32cSOlga Kornievskaia 	if (dst_server != src_server) {
2300e65a32cSOlga Kornievskaia 		spin_lock(&src_server->nfs_client->cl_lock);
2310e65a32cSOlga Kornievskaia 		list_del_init(&copy->src_copies);
2320e65a32cSOlga Kornievskaia 		spin_unlock(&src_server->nfs_client->cl_lock);
2330e65a32cSOlga Kornievskaia 	}
234c975c209SOlga Kornievskaia 	if (status == -ERESTARTSYS) {
235e4648aa4SOlga Kornievskaia 		goto out_cancel;
2360e65a32cSOlga Kornievskaia 	} else if (copy->flags || copy->error == NFS4ERR_PARTNER_NO_AUTH) {
237e4648aa4SOlga Kornievskaia 		status = -EAGAIN;
2380e65a32cSOlga Kornievskaia 		*restart = true;
239e4648aa4SOlga Kornievskaia 		goto out_cancel;
240c975c209SOlga Kornievskaia 	}
241bc0c9079SOlga Kornievskaia out:
24262164f31SOlga Kornievskaia 	res->write_res.count = copy->count;
24362164f31SOlga Kornievskaia 	memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
24462164f31SOlga Kornievskaia 	status = -copy->error;
24562164f31SOlga Kornievskaia 
24612406025SOlga Kornievskaia out_free:
24762164f31SOlga Kornievskaia 	kfree(copy);
24862164f31SOlga Kornievskaia 	return status;
249e4648aa4SOlga Kornievskaia out_cancel:
250e4648aa4SOlga Kornievskaia 	nfs42_do_offload_cancel_async(dst, &copy->stateid);
25112406025SOlga Kornievskaia 	if (!nfs42_files_from_same_server(src, dst))
25212406025SOlga Kornievskaia 		nfs42_do_offload_cancel_async(src, src_stateid);
25312406025SOlga Kornievskaia 	goto out_free;
25462164f31SOlga Kornievskaia }
25562164f31SOlga Kornievskaia 
process_copy_commit(struct file * dst,loff_t pos_dst,struct nfs42_copy_res * res)2566b8d84e2SOlga Kornievskaia static int process_copy_commit(struct file *dst, loff_t pos_dst,
2576b8d84e2SOlga Kornievskaia 			       struct nfs42_copy_res *res)
2586b8d84e2SOlga Kornievskaia {
2596b8d84e2SOlga Kornievskaia 	struct nfs_commitres cres;
2606b8d84e2SOlga Kornievskaia 	int status = -ENOMEM;
2616b8d84e2SOlga Kornievskaia 
2624fb547beSTrond Myklebust 	cres.verf = kzalloc(sizeof(struct nfs_writeverf), GFP_KERNEL);
2636b8d84e2SOlga Kornievskaia 	if (!cres.verf)
2646b8d84e2SOlga Kornievskaia 		goto out;
2656b8d84e2SOlga Kornievskaia 
2666b8d84e2SOlga Kornievskaia 	status = nfs4_proc_commit(dst, pos_dst, res->write_res.count, &cres);
2676b8d84e2SOlga Kornievskaia 	if (status)
2686b8d84e2SOlga Kornievskaia 		goto out_free;
2696b8d84e2SOlga Kornievskaia 	if (nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
2706b8d84e2SOlga Kornievskaia 				    &cres.verf->verifier)) {
2716b8d84e2SOlga Kornievskaia 		dprintk("commit verf differs from copy verf\n");
2726b8d84e2SOlga Kornievskaia 		status = -EAGAIN;
2736b8d84e2SOlga Kornievskaia 	}
2746b8d84e2SOlga Kornievskaia out_free:
2756b8d84e2SOlga Kornievskaia 	kfree(cres.verf);
2766b8d84e2SOlga Kornievskaia out:
2776b8d84e2SOlga Kornievskaia 	return status;
2786b8d84e2SOlga Kornievskaia }
2796b8d84e2SOlga Kornievskaia 
28094d202d5STrond Myklebust /**
28194d202d5STrond Myklebust  * nfs42_copy_dest_done - perform inode cache updates after clone/copy offload
28294d202d5STrond Myklebust  * @inode: pointer to destination inode
28394d202d5STrond Myklebust  * @pos: destination offset
28494d202d5STrond Myklebust  * @len: copy length
28594d202d5STrond Myklebust  *
28694d202d5STrond Myklebust  * Punch a hole in the inode page cache, so that the NFS client will
28794d202d5STrond Myklebust  * know to retrieve new data.
28894d202d5STrond Myklebust  * Update the file size if necessary, and then mark the inode as having
28994d202d5STrond Myklebust  * invalid cached values for change attribute, ctime, mtime and space used.
29094d202d5STrond Myklebust  */
nfs42_copy_dest_done(struct inode * inode,loff_t pos,loff_t len)29194d202d5STrond Myklebust static void nfs42_copy_dest_done(struct inode *inode, loff_t pos, loff_t len)
29294d202d5STrond Myklebust {
29394d202d5STrond Myklebust 	loff_t newsize = pos + len;
29494d202d5STrond Myklebust 	loff_t end = newsize - 1;
29594d202d5STrond Myklebust 
2963f015d89SBenjamin Coddington 	WARN_ON_ONCE(invalidate_inode_pages2_range(inode->i_mapping,
2973f015d89SBenjamin Coddington 				pos >> PAGE_SHIFT, end >> PAGE_SHIFT));
2983f015d89SBenjamin Coddington 
29994d202d5STrond Myklebust 	spin_lock(&inode->i_lock);
30094d202d5STrond Myklebust 	if (newsize > i_size_read(inode))
30194d202d5STrond Myklebust 		i_size_write(inode, newsize);
30294d202d5STrond Myklebust 	nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE |
30394d202d5STrond Myklebust 					     NFS_INO_INVALID_CTIME |
30494d202d5STrond Myklebust 					     NFS_INO_INVALID_MTIME |
30594d202d5STrond Myklebust 					     NFS_INO_INVALID_BLOCKS);
30694d202d5STrond Myklebust 	spin_unlock(&inode->i_lock);
30794d202d5STrond Myklebust }
30894d202d5STrond Myklebust 
_nfs42_proc_copy(struct file * src,struct nfs_lock_context * src_lock,struct file * dst,struct nfs_lock_context * dst_lock,struct nfs42_copy_args * args,struct nfs42_copy_res * res,struct nl4_server * nss,nfs4_stateid * cnr_stateid,bool * restart)3099d8cacbfSTrond Myklebust static ssize_t _nfs42_proc_copy(struct file *src,
3102e72448bSAnna Schumaker 				struct nfs_lock_context *src_lock,
3119d8cacbfSTrond Myklebust 				struct file *dst,
3122e72448bSAnna Schumaker 				struct nfs_lock_context *dst_lock,
3139d8cacbfSTrond Myklebust 				struct nfs42_copy_args *args,
3141d38f3f0SOlga Kornievskaia 				struct nfs42_copy_res *res,
3151d38f3f0SOlga Kornievskaia 				struct nl4_server *nss,
3160e65a32cSOlga Kornievskaia 				nfs4_stateid *cnr_stateid,
3170e65a32cSOlga Kornievskaia 				bool *restart)
3182e72448bSAnna Schumaker {
3192e72448bSAnna Schumaker 	struct rpc_message msg = {
3202e72448bSAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY],
3219d8cacbfSTrond Myklebust 		.rpc_argp = args,
3229d8cacbfSTrond Myklebust 		.rpc_resp = res,
3232e72448bSAnna Schumaker 	};
3242e72448bSAnna Schumaker 	struct inode *dst_inode = file_inode(dst);
3250e65a32cSOlga Kornievskaia 	struct inode *src_inode = file_inode(src);
3260e65a32cSOlga Kornievskaia 	struct nfs_server *dst_server = NFS_SERVER(dst_inode);
3270e65a32cSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(src_inode);
3289d8cacbfSTrond Myklebust 	loff_t pos_src = args->src_pos;
3299d8cacbfSTrond Myklebust 	loff_t pos_dst = args->dst_pos;
3309d8cacbfSTrond Myklebust 	size_t count = args->count;
3311ee48bddSOlga Kornievskaia 	ssize_t status;
3322e72448bSAnna Schumaker 
3331d38f3f0SOlga Kornievskaia 	if (nss) {
3341d38f3f0SOlga Kornievskaia 		args->cp_src = nss;
3351d38f3f0SOlga Kornievskaia 		nfs4_stateid_copy(&args->src_stateid, cnr_stateid);
3361d38f3f0SOlga Kornievskaia 	} else {
3371d38f3f0SOlga Kornievskaia 		status = nfs4_set_rw_stateid(&args->src_stateid,
3381d38f3f0SOlga Kornievskaia 				src_lock->open_context, src_lock, FMODE_READ);
339d826e5b8SOlga Kornievskaia 		if (status) {
340d826e5b8SOlga Kornievskaia 			if (status == -EAGAIN)
341d826e5b8SOlga Kornievskaia 				status = -NFS4ERR_BAD_STATEID;
3422e72448bSAnna Schumaker 			return status;
3431d38f3f0SOlga Kornievskaia 		}
344d826e5b8SOlga Kornievskaia 	}
345265a04b0SAl Viro 	status = nfs_filemap_write_and_wait_range(src->f_mapping,
346837bb1d7STrond Myklebust 			pos_src, pos_src + (loff_t)count - 1);
347837bb1d7STrond Myklebust 	if (status)
348837bb1d7STrond Myklebust 		return status;
349837bb1d7STrond Myklebust 
3509d8cacbfSTrond Myklebust 	status = nfs4_set_rw_stateid(&args->dst_stateid, dst_lock->open_context,
3512e72448bSAnna Schumaker 				     dst_lock, FMODE_WRITE);
352d826e5b8SOlga Kornievskaia 	if (status) {
353d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
354d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
3552e72448bSAnna Schumaker 		return status;
356d826e5b8SOlga Kornievskaia 	}
3572e72448bSAnna Schumaker 
358837bb1d7STrond Myklebust 	status = nfs_sync_inode(dst_inode);
359837bb1d7STrond Myklebust 	if (status)
360837bb1d7STrond Myklebust 		return status;
361837bb1d7STrond Myklebust 
36262164f31SOlga Kornievskaia 	res->commit_res.verf = NULL;
36362164f31SOlga Kornievskaia 	if (args->sync) {
36462164f31SOlga Kornievskaia 		res->commit_res.verf =
3654fb547beSTrond Myklebust 			kzalloc(sizeof(struct nfs_writeverf), GFP_KERNEL);
366e0926934SOlga Kornievskaia 		if (!res->commit_res.verf)
367e0926934SOlga Kornievskaia 			return -ENOMEM;
36862164f31SOlga Kornievskaia 	}
3690e65a32cSOlga Kornievskaia 	set_bit(NFS_CLNT_SRC_SSC_COPY_STATE,
3700e65a32cSOlga Kornievskaia 		&src_lock->open_context->state->flags);
371e4648aa4SOlga Kornievskaia 	set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
372e4648aa4SOlga Kornievskaia 		&dst_lock->open_context->state->flags);
373e4648aa4SOlga Kornievskaia 
3740e65a32cSOlga Kornievskaia 	status = nfs4_call_sync(dst_server->client, dst_server, &msg,
3759d8cacbfSTrond Myklebust 				&args->seq_args, &res->seq_res, 0);
376ce7cea1bSOlga Kornievskaia 	trace_nfs4_copy(src_inode, dst_inode, args, res, nss, status);
3772e72448bSAnna Schumaker 	if (status == -ENOTSUPP)
3780e65a32cSOlga Kornievskaia 		dst_server->caps &= ~NFS_CAP_COPY;
3792e72448bSAnna Schumaker 	if (status)
380e0926934SOlga Kornievskaia 		goto out;
3812e72448bSAnna Schumaker 
38262164f31SOlga Kornievskaia 	if (args->sync &&
38362164f31SOlga Kornievskaia 		nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
384e0926934SOlga Kornievskaia 				    &res->commit_res.verf->verifier)) {
385e0926934SOlga Kornievskaia 		status = -EAGAIN;
386e0926934SOlga Kornievskaia 		goto out;
3872e72448bSAnna Schumaker 	}
3882e72448bSAnna Schumaker 
38962164f31SOlga Kornievskaia 	if (!res->synchronous) {
3900e65a32cSOlga Kornievskaia 		status = handle_async_copy(res, dst_server, src_server, src,
3910e65a32cSOlga Kornievskaia 				dst, &args->src_stateid, restart);
39262164f31SOlga Kornievskaia 		if (status)
393123c23c6SWenwen Wang 			goto out;
39462164f31SOlga Kornievskaia 	}
39562164f31SOlga Kornievskaia 
3966b8d84e2SOlga Kornievskaia 	if ((!res->synchronous || !args->sync) &&
3976b8d84e2SOlga Kornievskaia 			res->write_res.verifier.committed != NFS_FILE_SYNC) {
3986b8d84e2SOlga Kornievskaia 		status = process_copy_commit(dst, pos_dst, res);
3996b8d84e2SOlga Kornievskaia 		if (status)
400123c23c6SWenwen Wang 			goto out;
4016b8d84e2SOlga Kornievskaia 	}
4026b8d84e2SOlga Kornievskaia 
40394d202d5STrond Myklebust 	nfs42_copy_dest_done(dst_inode, pos_dst, res->write_res.count);
404febfeaaeSTrond Myklebust 	nfs_invalidate_atime(src_inode);
405e0926934SOlga Kornievskaia 	status = res->write_res.count;
406e0926934SOlga Kornievskaia out:
40762164f31SOlga Kornievskaia 	if (args->sync)
408e0926934SOlga Kornievskaia 		kfree(res->commit_res.verf);
409e0926934SOlga Kornievskaia 	return status;
4102e72448bSAnna Schumaker }
4112e72448bSAnna Schumaker 
nfs42_proc_copy(struct file * src,loff_t pos_src,struct file * dst,loff_t pos_dst,size_t count,struct nl4_server * nss,nfs4_stateid * cnr_stateid,bool sync)4122e72448bSAnna Schumaker ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
4131d38f3f0SOlga Kornievskaia 			struct file *dst, loff_t pos_dst, size_t count,
4141d38f3f0SOlga Kornievskaia 			struct nl4_server *nss,
41512751010SOlga Kornievskaia 			nfs4_stateid *cnr_stateid, bool sync)
4162e72448bSAnna Schumaker {
4172e72448bSAnna Schumaker 	struct nfs_server *server = NFS_SERVER(file_inode(dst));
4182e72448bSAnna Schumaker 	struct nfs_lock_context *src_lock;
4192e72448bSAnna Schumaker 	struct nfs_lock_context *dst_lock;
4209d8cacbfSTrond Myklebust 	struct nfs42_copy_args args = {
4219d8cacbfSTrond Myklebust 		.src_fh		= NFS_FH(file_inode(src)),
4229d8cacbfSTrond Myklebust 		.src_pos	= pos_src,
4239d8cacbfSTrond Myklebust 		.dst_fh		= NFS_FH(file_inode(dst)),
4249d8cacbfSTrond Myklebust 		.dst_pos	= pos_dst,
4259d8cacbfSTrond Myklebust 		.count		= count,
42612751010SOlga Kornievskaia 		.sync		= sync,
4279d8cacbfSTrond Myklebust 	};
4289d8cacbfSTrond Myklebust 	struct nfs42_copy_res res;
4299d8cacbfSTrond Myklebust 	struct nfs4_exception src_exception = {
4309d8cacbfSTrond Myklebust 		.inode		= file_inode(src),
4319d8cacbfSTrond Myklebust 		.stateid	= &args.src_stateid,
4329d8cacbfSTrond Myklebust 	};
4339d8cacbfSTrond Myklebust 	struct nfs4_exception dst_exception = {
4349d8cacbfSTrond Myklebust 		.inode		= file_inode(dst),
4359d8cacbfSTrond Myklebust 		.stateid	= &args.dst_stateid,
4369d8cacbfSTrond Myklebust 	};
4372e72448bSAnna Schumaker 	ssize_t err, err2;
4380e65a32cSOlga Kornievskaia 	bool restart = false;
4392e72448bSAnna Schumaker 
4402e72448bSAnna Schumaker 	src_lock = nfs_get_lock_context(nfs_file_open_context(src));
4412e72448bSAnna Schumaker 	if (IS_ERR(src_lock))
4422e72448bSAnna Schumaker 		return PTR_ERR(src_lock);
4432e72448bSAnna Schumaker 
4442e72448bSAnna Schumaker 	src_exception.state = src_lock->open_context->state;
4452e72448bSAnna Schumaker 
4462e72448bSAnna Schumaker 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst));
4472e72448bSAnna Schumaker 	if (IS_ERR(dst_lock)) {
4482e72448bSAnna Schumaker 		err = PTR_ERR(dst_lock);
4492e72448bSAnna Schumaker 		goto out_put_src_lock;
4502e72448bSAnna Schumaker 	}
4512e72448bSAnna Schumaker 
4522e72448bSAnna Schumaker 	dst_exception.state = dst_lock->open_context->state;
4532e72448bSAnna Schumaker 
4542e72448bSAnna Schumaker 	do {
455ea8ea737SLinus Torvalds 		inode_lock(file_inode(dst));
4569d8cacbfSTrond Myklebust 		err = _nfs42_proc_copy(src, src_lock,
4579d8cacbfSTrond Myklebust 				dst, dst_lock,
4581d38f3f0SOlga Kornievskaia 				&args, &res,
4590e65a32cSOlga Kornievskaia 				nss, cnr_stateid, &restart);
460ea8ea737SLinus Torvalds 		inode_unlock(file_inode(dst));
4612e72448bSAnna Schumaker 
4629d8cacbfSTrond Myklebust 		if (err >= 0)
4639d8cacbfSTrond Myklebust 			break;
46447305153STigran Mkrtchyan 		if ((err == -ENOTSUPP ||
46547305153STigran Mkrtchyan 				err == -NFS4ERR_OFFLOAD_DENIED) &&
46612406025SOlga Kornievskaia 				nfs42_files_from_same_server(src, dst)) {
4672e72448bSAnna Schumaker 			err = -EOPNOTSUPP;
4682e72448bSAnna Schumaker 			break;
469539f57b3SOlga Kornievskaia 		} else if (err == -EAGAIN) {
4700e65a32cSOlga Kornievskaia 			if (!restart) {
471539f57b3SOlga Kornievskaia 				dst_exception.retry = 1;
472539f57b3SOlga Kornievskaia 				continue;
4730e65a32cSOlga Kornievskaia 			}
4740e65a32cSOlga Kornievskaia 			break;
4755690eed9SOlga Kornievskaia 		} else if (err == -NFS4ERR_OFFLOAD_NO_REQS &&
4765690eed9SOlga Kornievskaia 				args.sync != res.synchronous) {
4775690eed9SOlga Kornievskaia 			args.sync = res.synchronous;
478e0926934SOlga Kornievskaia 			dst_exception.retry = 1;
479e0926934SOlga Kornievskaia 			continue;
4806b61c969SOlga Kornievskaia 		} else if ((err == -ESTALE ||
48112406025SOlga Kornievskaia 				err == -NFS4ERR_OFFLOAD_DENIED ||
48212406025SOlga Kornievskaia 				err == -ENOTSUPP) &&
4837e350197SOlga Kornievskaia 				!nfs42_files_from_same_server(src, dst)) {
4847e350197SOlga Kornievskaia 			nfs42_do_offload_cancel_async(src, &args.src_stateid);
4857e350197SOlga Kornievskaia 			err = -EOPNOTSUPP;
4867e350197SOlga Kornievskaia 			break;
4872e72448bSAnna Schumaker 		}
4882e72448bSAnna Schumaker 
4892e72448bSAnna Schumaker 		err2 = nfs4_handle_exception(server, err, &src_exception);
4902e72448bSAnna Schumaker 		err  = nfs4_handle_exception(server, err, &dst_exception);
4912e72448bSAnna Schumaker 		if (!err)
4922e72448bSAnna Schumaker 			err = err2;
4932e72448bSAnna Schumaker 	} while (src_exception.retry || dst_exception.retry);
4942e72448bSAnna Schumaker 
4952e72448bSAnna Schumaker 	nfs_put_lock_context(dst_lock);
4962e72448bSAnna Schumaker out_put_src_lock:
4972e72448bSAnna Schumaker 	nfs_put_lock_context(src_lock);
4982e72448bSAnna Schumaker 	return err;
4992e72448bSAnna Schumaker }
5002e72448bSAnna Schumaker 
501c975c209SOlga Kornievskaia struct nfs42_offloadcancel_data {
502c975c209SOlga Kornievskaia 	struct nfs_server *seq_server;
503c975c209SOlga Kornievskaia 	struct nfs42_offload_status_args args;
504c975c209SOlga Kornievskaia 	struct nfs42_offload_status_res res;
505c975c209SOlga Kornievskaia };
506c975c209SOlga Kornievskaia 
nfs42_offload_cancel_prepare(struct rpc_task * task,void * calldata)507c975c209SOlga Kornievskaia static void nfs42_offload_cancel_prepare(struct rpc_task *task, void *calldata)
508c975c209SOlga Kornievskaia {
509c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
510c975c209SOlga Kornievskaia 
511c975c209SOlga Kornievskaia 	nfs4_setup_sequence(data->seq_server->nfs_client,
512c975c209SOlga Kornievskaia 				&data->args.osa_seq_args,
513c975c209SOlga Kornievskaia 				&data->res.osr_seq_res, task);
514c975c209SOlga Kornievskaia }
515c975c209SOlga Kornievskaia 
nfs42_offload_cancel_done(struct rpc_task * task,void * calldata)516c975c209SOlga Kornievskaia static void nfs42_offload_cancel_done(struct rpc_task *task, void *calldata)
517c975c209SOlga Kornievskaia {
518c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = calldata;
519c975c209SOlga Kornievskaia 
520127becabSOlga Kornievskaia 	trace_nfs4_offload_cancel(&data->args, task->tk_status);
521c975c209SOlga Kornievskaia 	nfs41_sequence_done(task, &data->res.osr_seq_res);
522c975c209SOlga Kornievskaia 	if (task->tk_status &&
523c975c209SOlga Kornievskaia 		nfs4_async_handle_error(task, data->seq_server, NULL,
524c975c209SOlga Kornievskaia 			NULL) == -EAGAIN)
525c975c209SOlga Kornievskaia 		rpc_restart_call_prepare(task);
526c975c209SOlga Kornievskaia }
527c975c209SOlga Kornievskaia 
nfs42_free_offloadcancel_data(void * data)528c975c209SOlga Kornievskaia static void nfs42_free_offloadcancel_data(void *data)
529c975c209SOlga Kornievskaia {
530c975c209SOlga Kornievskaia 	kfree(data);
531c975c209SOlga Kornievskaia }
532c975c209SOlga Kornievskaia 
533c975c209SOlga Kornievskaia static const struct rpc_call_ops nfs42_offload_cancel_ops = {
534c975c209SOlga Kornievskaia 	.rpc_call_prepare = nfs42_offload_cancel_prepare,
535c975c209SOlga Kornievskaia 	.rpc_call_done = nfs42_offload_cancel_done,
536c975c209SOlga Kornievskaia 	.rpc_release = nfs42_free_offloadcancel_data,
537c975c209SOlga Kornievskaia };
538c975c209SOlga Kornievskaia 
nfs42_do_offload_cancel_async(struct file * dst,nfs4_stateid * stateid)539c975c209SOlga Kornievskaia static int nfs42_do_offload_cancel_async(struct file *dst,
540c975c209SOlga Kornievskaia 					 nfs4_stateid *stateid)
541c975c209SOlga Kornievskaia {
542c975c209SOlga Kornievskaia 	struct nfs_server *dst_server = NFS_SERVER(file_inode(dst));
543c975c209SOlga Kornievskaia 	struct nfs42_offloadcancel_data *data = NULL;
544c975c209SOlga Kornievskaia 	struct nfs_open_context *ctx = nfs_file_open_context(dst);
545c975c209SOlga Kornievskaia 	struct rpc_task *task;
546c975c209SOlga Kornievskaia 	struct rpc_message msg = {
547c975c209SOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OFFLOAD_CANCEL],
548c975c209SOlga Kornievskaia 		.rpc_cred = ctx->cred,
549c975c209SOlga Kornievskaia 	};
550c975c209SOlga Kornievskaia 	struct rpc_task_setup task_setup_data = {
551c975c209SOlga Kornievskaia 		.rpc_client = dst_server->client,
552c975c209SOlga Kornievskaia 		.rpc_message = &msg,
553c975c209SOlga Kornievskaia 		.callback_ops = &nfs42_offload_cancel_ops,
554c975c209SOlga Kornievskaia 		.workqueue = nfsiod_workqueue,
555c975c209SOlga Kornievskaia 		.flags = RPC_TASK_ASYNC,
556c975c209SOlga Kornievskaia 	};
557c975c209SOlga Kornievskaia 	int status;
558c975c209SOlga Kornievskaia 
559c975c209SOlga Kornievskaia 	if (!(dst_server->caps & NFS_CAP_OFFLOAD_CANCEL))
560c975c209SOlga Kornievskaia 		return -EOPNOTSUPP;
561c975c209SOlga Kornievskaia 
5624fb547beSTrond Myklebust 	data = kzalloc(sizeof(struct nfs42_offloadcancel_data), GFP_KERNEL);
563c975c209SOlga Kornievskaia 	if (data == NULL)
564c975c209SOlga Kornievskaia 		return -ENOMEM;
565c975c209SOlga Kornievskaia 
566c975c209SOlga Kornievskaia 	data->seq_server = dst_server;
567c975c209SOlga Kornievskaia 	data->args.osa_src_fh = NFS_FH(file_inode(dst));
568c975c209SOlga Kornievskaia 	memcpy(&data->args.osa_stateid, stateid,
569c975c209SOlga Kornievskaia 		sizeof(data->args.osa_stateid));
570c975c209SOlga Kornievskaia 	msg.rpc_argp = &data->args;
571c975c209SOlga Kornievskaia 	msg.rpc_resp = &data->res;
572c975c209SOlga Kornievskaia 	task_setup_data.callback_data = data;
573c975c209SOlga Kornievskaia 	nfs4_init_sequence(&data->args.osa_seq_args, &data->res.osr_seq_res,
574c975c209SOlga Kornievskaia 			   1, 0);
575c975c209SOlga Kornievskaia 	task = rpc_run_task(&task_setup_data);
576c975c209SOlga Kornievskaia 	if (IS_ERR(task))
577c975c209SOlga Kornievskaia 		return PTR_ERR(task);
578c975c209SOlga Kornievskaia 	status = rpc_wait_for_completion_task(task);
579c975c209SOlga Kornievskaia 	if (status == -ENOTSUPP)
580c975c209SOlga Kornievskaia 		dst_server->caps &= ~NFS_CAP_OFFLOAD_CANCEL;
581c975c209SOlga Kornievskaia 	rpc_put_task(task);
582c975c209SOlga Kornievskaia 	return status;
583c975c209SOlga Kornievskaia }
584c975c209SOlga Kornievskaia 
_nfs42_proc_copy_notify(struct file * src,struct file * dst,struct nfs42_copy_notify_args * args,struct nfs42_copy_notify_res * res)58500030104SYueHaibing static int _nfs42_proc_copy_notify(struct file *src, struct file *dst,
5860491567bSOlga Kornievskaia 				   struct nfs42_copy_notify_args *args,
5870491567bSOlga Kornievskaia 				   struct nfs42_copy_notify_res *res)
5880491567bSOlga Kornievskaia {
5890491567bSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
5900491567bSOlga Kornievskaia 	struct rpc_message msg = {
5910491567bSOlga Kornievskaia 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY_NOTIFY],
5920491567bSOlga Kornievskaia 		.rpc_argp = args,
5930491567bSOlga Kornievskaia 		.rpc_resp = res,
5940491567bSOlga Kornievskaia 	};
5950491567bSOlga Kornievskaia 	int status;
5960491567bSOlga Kornievskaia 	struct nfs_open_context *ctx;
5970491567bSOlga Kornievskaia 	struct nfs_lock_context *l_ctx;
5980491567bSOlga Kornievskaia 
5990491567bSOlga Kornievskaia 	ctx = get_nfs_open_context(nfs_file_open_context(src));
6000491567bSOlga Kornievskaia 	l_ctx = nfs_get_lock_context(ctx);
601b7f114edSXin Xiong 	if (IS_ERR(l_ctx)) {
602b7f114edSXin Xiong 		status = PTR_ERR(l_ctx);
603b7f114edSXin Xiong 		goto out;
604b7f114edSXin Xiong 	}
6050491567bSOlga Kornievskaia 
6060491567bSOlga Kornievskaia 	status = nfs4_set_rw_stateid(&args->cna_src_stateid, ctx, l_ctx,
6070491567bSOlga Kornievskaia 				     FMODE_READ);
6080491567bSOlga Kornievskaia 	nfs_put_lock_context(l_ctx);
609d826e5b8SOlga Kornievskaia 	if (status) {
610d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
611d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
612b7f114edSXin Xiong 		goto out;
613d826e5b8SOlga Kornievskaia 	}
6140491567bSOlga Kornievskaia 
6150491567bSOlga Kornievskaia 	status = nfs4_call_sync(src_server->client, src_server, &msg,
6160491567bSOlga Kornievskaia 				&args->cna_seq_args, &res->cnr_seq_res, 0);
617488b170cSOlga Kornievskaia 	trace_nfs4_copy_notify(file_inode(src), args, res, status);
6180491567bSOlga Kornievskaia 	if (status == -ENOTSUPP)
6190491567bSOlga Kornievskaia 		src_server->caps &= ~NFS_CAP_COPY_NOTIFY;
6200491567bSOlga Kornievskaia 
621b7f114edSXin Xiong out:
6220491567bSOlga Kornievskaia 	put_nfs_open_context(nfs_file_open_context(src));
6230491567bSOlga Kornievskaia 	return status;
6240491567bSOlga Kornievskaia }
6250491567bSOlga Kornievskaia 
nfs42_proc_copy_notify(struct file * src,struct file * dst,struct nfs42_copy_notify_res * res)6260491567bSOlga Kornievskaia int nfs42_proc_copy_notify(struct file *src, struct file *dst,
6270491567bSOlga Kornievskaia 				struct nfs42_copy_notify_res *res)
6280491567bSOlga Kornievskaia {
6290491567bSOlga Kornievskaia 	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
6300491567bSOlga Kornievskaia 	struct nfs42_copy_notify_args *args;
6310491567bSOlga Kornievskaia 	struct nfs4_exception exception = {
6320491567bSOlga Kornievskaia 		.inode = file_inode(src),
6330491567bSOlga Kornievskaia 	};
6340491567bSOlga Kornievskaia 	int status;
6350491567bSOlga Kornievskaia 
6360491567bSOlga Kornievskaia 	if (!(src_server->caps & NFS_CAP_COPY_NOTIFY))
6370491567bSOlga Kornievskaia 		return -EOPNOTSUPP;
6380491567bSOlga Kornievskaia 
6394fb547beSTrond Myklebust 	args = kzalloc(sizeof(struct nfs42_copy_notify_args), GFP_KERNEL);
6400491567bSOlga Kornievskaia 	if (args == NULL)
6410491567bSOlga Kornievskaia 		return -ENOMEM;
6420491567bSOlga Kornievskaia 
6430491567bSOlga Kornievskaia 	args->cna_src_fh  = NFS_FH(file_inode(src)),
6440491567bSOlga Kornievskaia 	args->cna_dst.nl4_type = NL4_NETADDR;
6450491567bSOlga Kornievskaia 	nfs42_set_netaddr(dst, &args->cna_dst.u.nl4_addr);
6460491567bSOlga Kornievskaia 	exception.stateid = &args->cna_src_stateid;
6470491567bSOlga Kornievskaia 
6480491567bSOlga Kornievskaia 	do {
6490491567bSOlga Kornievskaia 		status = _nfs42_proc_copy_notify(src, dst, args, res);
6500491567bSOlga Kornievskaia 		if (status == -ENOTSUPP) {
6510491567bSOlga Kornievskaia 			status = -EOPNOTSUPP;
6520491567bSOlga Kornievskaia 			goto out;
6530491567bSOlga Kornievskaia 		}
6540491567bSOlga Kornievskaia 		status = nfs4_handle_exception(src_server, status, &exception);
6550491567bSOlga Kornievskaia 	} while (exception.retry);
6560491567bSOlga Kornievskaia 
6570491567bSOlga Kornievskaia out:
6580491567bSOlga Kornievskaia 	kfree(args);
6590491567bSOlga Kornievskaia 	return status;
6600491567bSOlga Kornievskaia }
6610491567bSOlga Kornievskaia 
_nfs42_proc_llseek(struct file * filep,struct nfs_lock_context * lock,loff_t offset,int whence)6624bdf87ebSChristoph Hellwig static loff_t _nfs42_proc_llseek(struct file *filep,
6634bdf87ebSChristoph Hellwig 		struct nfs_lock_context *lock, loff_t offset, int whence)
6641c6dcbe5SAnna Schumaker {
6651c6dcbe5SAnna Schumaker 	struct inode *inode = file_inode(filep);
6661c6dcbe5SAnna Schumaker 	struct nfs42_seek_args args = {
6671c6dcbe5SAnna Schumaker 		.sa_fh		= NFS_FH(inode),
6681c6dcbe5SAnna Schumaker 		.sa_offset	= offset,
6691c6dcbe5SAnna Schumaker 		.sa_what	= (whence == SEEK_HOLE) ?
6701c6dcbe5SAnna Schumaker 					NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA,
6711c6dcbe5SAnna Schumaker 	};
6721c6dcbe5SAnna Schumaker 	struct nfs42_seek_res res;
6731c6dcbe5SAnna Schumaker 	struct rpc_message msg = {
6741c6dcbe5SAnna Schumaker 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK],
6751c6dcbe5SAnna Schumaker 		.rpc_argp = &args,
6761c6dcbe5SAnna Schumaker 		.rpc_resp = &res,
6771c6dcbe5SAnna Schumaker 	};
6781c6dcbe5SAnna Schumaker 	struct nfs_server *server = NFS_SERVER(inode);
6791c6dcbe5SAnna Schumaker 	int status;
6801c6dcbe5SAnna Schumaker 
681878ffa9fSAnna Schumaker 	if (!nfs_server_capable(inode, NFS_CAP_SEEK))
6821c6dcbe5SAnna Schumaker 		return -ENOTSUPP;
6831c6dcbe5SAnna Schumaker 
6844bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context,
6854bdf87ebSChristoph Hellwig 			lock, FMODE_READ);
686d826e5b8SOlga Kornievskaia 	if (status) {
687d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
688d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
6891c6dcbe5SAnna Schumaker 		return status;
690d826e5b8SOlga Kornievskaia 	}
6911c6dcbe5SAnna Schumaker 
692e95fc4a0STrond Myklebust 	status = nfs_filemap_write_and_wait_range(inode->i_mapping,
693e95fc4a0STrond Myklebust 			offset, LLONG_MAX);
694e95fc4a0STrond Myklebust 	if (status)
695e95fc4a0STrond Myklebust 		return status;
696e95fc4a0STrond Myklebust 
6971c6dcbe5SAnna Schumaker 	status = nfs4_call_sync(server->client, server, &msg,
6981c6dcbe5SAnna Schumaker 				&args.seq_args, &res.seq_res, 0);
699f628d462SOlga Kornievskaia 	trace_nfs4_llseek(inode, &args, &res, status);
7001c6dcbe5SAnna Schumaker 	if (status == -ENOTSUPP)
7011c6dcbe5SAnna Schumaker 		server->caps &= ~NFS_CAP_SEEK;
7021c6dcbe5SAnna Schumaker 	if (status)
7031c6dcbe5SAnna Schumaker 		return status;
7041c6dcbe5SAnna Schumaker 
70573f5c88fSOlga Kornievskaia 	if (whence == SEEK_DATA && res.sr_eof)
70673f5c88fSOlga Kornievskaia 		return -NFS4ERR_NXIO;
70773f5c88fSOlga Kornievskaia 	else
7081c6dcbe5SAnna Schumaker 		return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes);
7091c6dcbe5SAnna Schumaker }
710be3a5d23STrond Myklebust 
nfs42_proc_llseek(struct file * filep,loff_t offset,int whence)711bdcc2cd1SJ. Bruce Fields loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
712bdcc2cd1SJ. Bruce Fields {
713bdcc2cd1SJ. Bruce Fields 	struct nfs_server *server = NFS_SERVER(file_inode(filep));
714bdcc2cd1SJ. Bruce Fields 	struct nfs4_exception exception = { };
7154bdf87ebSChristoph Hellwig 	struct nfs_lock_context *lock;
716306a5549SJ. Bruce Fields 	loff_t err;
717bdcc2cd1SJ. Bruce Fields 
7184bdf87ebSChristoph Hellwig 	lock = nfs_get_lock_context(nfs_file_open_context(filep));
7194bdf87ebSChristoph Hellwig 	if (IS_ERR(lock))
7204bdf87ebSChristoph Hellwig 		return PTR_ERR(lock);
7214bdf87ebSChristoph Hellwig 
7224bdf87ebSChristoph Hellwig 	exception.inode = file_inode(filep);
7234bdf87ebSChristoph Hellwig 	exception.state = lock->open_context->state;
7244bdf87ebSChristoph Hellwig 
725bdcc2cd1SJ. Bruce Fields 	do {
7264bdf87ebSChristoph Hellwig 		err = _nfs42_proc_llseek(filep, lock, offset, whence);
727306a5549SJ. Bruce Fields 		if (err >= 0)
728306a5549SJ. Bruce Fields 			break;
7294bdf87ebSChristoph Hellwig 		if (err == -ENOTSUPP) {
7304bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
7314bdf87ebSChristoph Hellwig 			break;
7324bdf87ebSChristoph Hellwig 		}
733bdcc2cd1SJ. Bruce Fields 		err = nfs4_handle_exception(server, err, &exception);
734bdcc2cd1SJ. Bruce Fields 	} while (exception.retry);
735bdcc2cd1SJ. Bruce Fields 
7364bdf87ebSChristoph Hellwig 	nfs_put_lock_context(lock);
737bdcc2cd1SJ. Bruce Fields 	return err;
738bdcc2cd1SJ. Bruce Fields }
739bdcc2cd1SJ. Bruce Fields 
740bdcc2cd1SJ. Bruce Fields 
7411b4a4bd8SPeng Tao static void
nfs42_layoutstat_prepare(struct rpc_task * task,void * calldata)7421b4a4bd8SPeng Tao nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata)
7431b4a4bd8SPeng Tao {
7441b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
7459a0fe867STrond Myklebust 	struct inode *inode = data->inode;
7469a0fe867STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
7479a0fe867STrond Myklebust 	struct pnfs_layout_hdr *lo;
7481b4a4bd8SPeng Tao 
7499a0fe867STrond Myklebust 	spin_lock(&inode->i_lock);
7509a0fe867STrond Myklebust 	lo = NFS_I(inode)->layout;
7519a0fe867STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
7529a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
7539a0fe867STrond Myklebust 		rpc_exit(task, 0);
7549a0fe867STrond Myklebust 		return;
7559a0fe867STrond Myklebust 	}
7569a0fe867STrond Myklebust 	nfs4_stateid_copy(&data->args.stateid, &lo->plh_stateid);
7579a0fe867STrond Myklebust 	spin_unlock(&inode->i_lock);
7586de7e12fSAnna Schumaker 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
7596de7e12fSAnna Schumaker 			    &data->res.seq_res, task);
7601b4a4bd8SPeng Tao }
7611b4a4bd8SPeng Tao 
7621b4a4bd8SPeng Tao static void
nfs42_layoutstat_done(struct rpc_task * task,void * calldata)7631b4a4bd8SPeng Tao nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
7641b4a4bd8SPeng Tao {
7651b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
76668d264cfSPeng Tao 	struct inode *inode = data->inode;
76768d264cfSPeng Tao 	struct pnfs_layout_hdr *lo;
7681b4a4bd8SPeng Tao 
7691b4a4bd8SPeng Tao 	if (!nfs4_sequence_done(task, &data->res.seq_res))
7701b4a4bd8SPeng Tao 		return;
7711b4a4bd8SPeng Tao 
7726c5a0d89STrond Myklebust 	switch (task->tk_status) {
7736c5a0d89STrond Myklebust 	case 0:
774638037b1STrond Myklebust 		return;
775cf61eb26STrond Myklebust 	case -NFS4ERR_BADHANDLE:
776cf61eb26STrond Myklebust 	case -ESTALE:
777cf61eb26STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
778cf61eb26STrond Myklebust 		break;
77968d264cfSPeng Tao 	case -NFS4ERR_EXPIRED:
780206b3bb5STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
781206b3bb5STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
78268d264cfSPeng Tao 	case -NFS4ERR_STALE_STATEID:
78368d264cfSPeng Tao 	case -NFS4ERR_BAD_STATEID:
78468d264cfSPeng Tao 		spin_lock(&inode->i_lock);
78568d264cfSPeng Tao 		lo = NFS_I(inode)->layout;
7869a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
7879a0fe867STrond Myklebust 		    nfs4_stateid_match(&data->args.stateid,
78868d264cfSPeng Tao 					     &lo->plh_stateid)) {
78968d264cfSPeng Tao 			LIST_HEAD(head);
79068d264cfSPeng Tao 
79168d264cfSPeng Tao 			/*
79268d264cfSPeng Tao 			 * Mark the bad layout state as invalid, then retry
79368d264cfSPeng Tao 			 * with the current stateid.
79468d264cfSPeng Tao 			 */
7955f46be04STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
79668d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
79768d264cfSPeng Tao 			pnfs_free_lseg_list(&head);
7981f18b82cSTrond Myklebust 			nfs_commit_inode(inode, 0);
79968d264cfSPeng Tao 		} else
80068d264cfSPeng Tao 			spin_unlock(&inode->i_lock);
80168d264cfSPeng Tao 		break;
8029a0fe867STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
8039a0fe867STrond Myklebust 		spin_lock(&inode->i_lock);
8049a0fe867STrond Myklebust 		lo = NFS_I(inode)->layout;
8059a0fe867STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
8069a0fe867STrond Myklebust 		    nfs4_stateid_match_other(&data->args.stateid,
8079a0fe867STrond Myklebust 					&lo->plh_stateid)) {
8089a0fe867STrond Myklebust 			/* Do we need to delay before resending? */
8099a0fe867STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
8109a0fe867STrond Myklebust 						&data->args.stateid))
8119a0fe867STrond Myklebust 				rpc_delay(task, HZ);
8129a0fe867STrond Myklebust 			rpc_restart_call_prepare(task);
8139a0fe867STrond Myklebust 		}
8149a0fe867STrond Myklebust 		spin_unlock(&inode->i_lock);
8159a0fe867STrond Myklebust 		break;
8166c5a0d89STrond Myklebust 	case -ENOTSUPP:
8176c5a0d89STrond Myklebust 	case -EOPNOTSUPP:
81868d264cfSPeng Tao 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
8191b4a4bd8SPeng Tao 	}
820638037b1STrond Myklebust 
821638037b1STrond Myklebust 	trace_nfs4_layoutstats(inode, &data->args.stateid, task->tk_status);
8226c5a0d89STrond Myklebust }
8231b4a4bd8SPeng Tao 
8241b4a4bd8SPeng Tao static void
nfs42_layoutstat_release(void * calldata)8251b4a4bd8SPeng Tao nfs42_layoutstat_release(void *calldata)
8261b4a4bd8SPeng Tao {
8271b4a4bd8SPeng Tao 	struct nfs42_layoutstat_data *data = calldata;
828422c93c8STrond Myklebust 	struct nfs42_layoutstat_devinfo *devinfo = data->args.devinfo;
829422c93c8STrond Myklebust 	int i;
8308733408dSPeng Tao 
831422c93c8STrond Myklebust 	for (i = 0; i < data->args.num_dev; i++) {
832422c93c8STrond Myklebust 		if (devinfo[i].ld_private.ops && devinfo[i].ld_private.ops->free)
833422c93c8STrond Myklebust 			devinfo[i].ld_private.ops->free(&devinfo[i].ld_private);
834422c93c8STrond Myklebust 	}
8351b4a4bd8SPeng Tao 
8361b4a4bd8SPeng Tao 	pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout);
8371bfe3b25SPeng Tao 	smp_mb__before_atomic();
8381bfe3b25SPeng Tao 	clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags);
8391bfe3b25SPeng Tao 	smp_mb__after_atomic();
8401b4a4bd8SPeng Tao 	nfs_iput_and_deactive(data->inode);
8411b4a4bd8SPeng Tao 	kfree(data->args.devinfo);
8421b4a4bd8SPeng Tao 	kfree(data);
8431b4a4bd8SPeng Tao }
8441b4a4bd8SPeng Tao 
845be3a5d23STrond Myklebust static const struct rpc_call_ops nfs42_layoutstat_ops = {
8461b4a4bd8SPeng Tao 	.rpc_call_prepare = nfs42_layoutstat_prepare,
8471b4a4bd8SPeng Tao 	.rpc_call_done = nfs42_layoutstat_done,
8481b4a4bd8SPeng Tao 	.rpc_release = nfs42_layoutstat_release,
849be3a5d23STrond Myklebust };
850be3a5d23STrond Myklebust 
nfs42_proc_layoutstats_generic(struct nfs_server * server,struct nfs42_layoutstat_data * data)851be3a5d23STrond Myklebust int nfs42_proc_layoutstats_generic(struct nfs_server *server,
852be3a5d23STrond Myklebust 				   struct nfs42_layoutstat_data *data)
853be3a5d23STrond Myklebust {
854be3a5d23STrond Myklebust 	struct rpc_message msg = {
855be3a5d23STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS],
856be3a5d23STrond Myklebust 		.rpc_argp = &data->args,
857be3a5d23STrond Myklebust 		.rpc_resp = &data->res,
858be3a5d23STrond Myklebust 	};
859be3a5d23STrond Myklebust 	struct rpc_task_setup task_setup = {
860be3a5d23STrond Myklebust 		.rpc_client = server->client,
861be3a5d23STrond Myklebust 		.rpc_message = &msg,
862be3a5d23STrond Myklebust 		.callback_ops = &nfs42_layoutstat_ops,
863be3a5d23STrond Myklebust 		.callback_data = data,
864be3a5d23STrond Myklebust 		.flags = RPC_TASK_ASYNC,
865be3a5d23STrond Myklebust 	};
866be3a5d23STrond Myklebust 	struct rpc_task *task;
867be3a5d23STrond Myklebust 
8681b4a4bd8SPeng Tao 	data->inode = nfs_igrab_and_active(data->args.inode);
8691b4a4bd8SPeng Tao 	if (!data->inode) {
8701b4a4bd8SPeng Tao 		nfs42_layoutstat_release(data);
8711b4a4bd8SPeng Tao 		return -EAGAIN;
8721b4a4bd8SPeng Tao 	}
873fba83f34SAnna Schumaker 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
874be3a5d23STrond Myklebust 	task = rpc_run_task(&task_setup);
875be3a5d23STrond Myklebust 	if (IS_ERR(task))
876be3a5d23STrond Myklebust 		return PTR_ERR(task);
8773f807e5aSJeff Layton 	rpc_put_task(task);
878be3a5d23STrond Myklebust 	return 0;
879be3a5d23STrond Myklebust }
880e5341f3aSPeng Tao 
8813eb86093STrond Myklebust static struct nfs42_layouterror_data *
nfs42_alloc_layouterror_data(struct pnfs_layout_segment * lseg,gfp_t gfp_flags)8823eb86093STrond Myklebust nfs42_alloc_layouterror_data(struct pnfs_layout_segment *lseg, gfp_t gfp_flags)
8833eb86093STrond Myklebust {
8843eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
8853eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
8863eb86093STrond Myklebust 
8873eb86093STrond Myklebust 	data = kzalloc(sizeof(*data), gfp_flags);
8883eb86093STrond Myklebust 	if (data) {
8893eb86093STrond Myklebust 		data->args.inode = data->inode = nfs_igrab_and_active(inode);
8903eb86093STrond Myklebust 		if (data->inode) {
8913eb86093STrond Myklebust 			data->lseg = pnfs_get_lseg(lseg);
8923eb86093STrond Myklebust 			if (data->lseg)
8933eb86093STrond Myklebust 				return data;
8943eb86093STrond Myklebust 			nfs_iput_and_deactive(data->inode);
8953eb86093STrond Myklebust 		}
8963eb86093STrond Myklebust 		kfree(data);
8973eb86093STrond Myklebust 	}
8983eb86093STrond Myklebust 	return NULL;
8993eb86093STrond Myklebust }
9003eb86093STrond Myklebust 
9013eb86093STrond Myklebust static void
nfs42_free_layouterror_data(struct nfs42_layouterror_data * data)9023eb86093STrond Myklebust nfs42_free_layouterror_data(struct nfs42_layouterror_data *data)
9033eb86093STrond Myklebust {
9043eb86093STrond Myklebust 	pnfs_put_lseg(data->lseg);
9053eb86093STrond Myklebust 	nfs_iput_and_deactive(data->inode);
9063eb86093STrond Myklebust 	kfree(data);
9073eb86093STrond Myklebust }
9083eb86093STrond Myklebust 
9093eb86093STrond Myklebust static void
nfs42_layouterror_prepare(struct rpc_task * task,void * calldata)9103eb86093STrond Myklebust nfs42_layouterror_prepare(struct rpc_task *task, void *calldata)
9113eb86093STrond Myklebust {
9123eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
9133eb86093STrond Myklebust 	struct inode *inode = data->inode;
9143eb86093STrond Myklebust 	struct nfs_server *server = NFS_SERVER(inode);
9153eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
9163eb86093STrond Myklebust 	unsigned i;
9173eb86093STrond Myklebust 
9183eb86093STrond Myklebust 	spin_lock(&inode->i_lock);
9193eb86093STrond Myklebust 	if (!pnfs_layout_is_valid(lo)) {
9203eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
9213eb86093STrond Myklebust 		rpc_exit(task, 0);
9223eb86093STrond Myklebust 		return;
9233eb86093STrond Myklebust 	}
9243eb86093STrond Myklebust 	for (i = 0; i < data->args.num_errors; i++)
9253eb86093STrond Myklebust 		nfs4_stateid_copy(&data->args.errors[i].stateid,
9263eb86093STrond Myklebust 				&lo->plh_stateid);
9273eb86093STrond Myklebust 	spin_unlock(&inode->i_lock);
9283eb86093STrond Myklebust 	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
9293eb86093STrond Myklebust 			    &data->res.seq_res, task);
9303eb86093STrond Myklebust }
9313eb86093STrond Myklebust 
9323eb86093STrond Myklebust static void
nfs42_layouterror_done(struct rpc_task * task,void * calldata)9333eb86093STrond Myklebust nfs42_layouterror_done(struct rpc_task *task, void *calldata)
9343eb86093STrond Myklebust {
9353eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
9363eb86093STrond Myklebust 	struct inode *inode = data->inode;
9373eb86093STrond Myklebust 	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
9383eb86093STrond Myklebust 
9393eb86093STrond Myklebust 	if (!nfs4_sequence_done(task, &data->res.seq_res))
9403eb86093STrond Myklebust 		return;
9413eb86093STrond Myklebust 
9423eb86093STrond Myklebust 	switch (task->tk_status) {
9433eb86093STrond Myklebust 	case 0:
944638037b1STrond Myklebust 		return;
9453eb86093STrond Myklebust 	case -NFS4ERR_BADHANDLE:
9463eb86093STrond Myklebust 	case -ESTALE:
9473eb86093STrond Myklebust 		pnfs_destroy_layout(NFS_I(inode));
9483eb86093STrond Myklebust 		break;
9493eb86093STrond Myklebust 	case -NFS4ERR_EXPIRED:
9503eb86093STrond Myklebust 	case -NFS4ERR_ADMIN_REVOKED:
9513eb86093STrond Myklebust 	case -NFS4ERR_DELEG_REVOKED:
9523eb86093STrond Myklebust 	case -NFS4ERR_STALE_STATEID:
9533eb86093STrond Myklebust 	case -NFS4ERR_BAD_STATEID:
9543eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
9553eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
9563eb86093STrond Myklebust 		    nfs4_stateid_match(&data->args.errors[0].stateid,
9573eb86093STrond Myklebust 					     &lo->plh_stateid)) {
9583eb86093STrond Myklebust 			LIST_HEAD(head);
9593eb86093STrond Myklebust 
9603eb86093STrond Myklebust 			/*
9613eb86093STrond Myklebust 			 * Mark the bad layout state as invalid, then retry
9623eb86093STrond Myklebust 			 * with the current stateid.
9633eb86093STrond Myklebust 			 */
9643eb86093STrond Myklebust 			pnfs_mark_layout_stateid_invalid(lo, &head);
9653eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
9663eb86093STrond Myklebust 			pnfs_free_lseg_list(&head);
9673eb86093STrond Myklebust 			nfs_commit_inode(inode, 0);
9683eb86093STrond Myklebust 		} else
9693eb86093STrond Myklebust 			spin_unlock(&inode->i_lock);
9703eb86093STrond Myklebust 		break;
9713eb86093STrond Myklebust 	case -NFS4ERR_OLD_STATEID:
9723eb86093STrond Myklebust 		spin_lock(&inode->i_lock);
9733eb86093STrond Myklebust 		if (pnfs_layout_is_valid(lo) &&
9743eb86093STrond Myklebust 		    nfs4_stateid_match_other(&data->args.errors[0].stateid,
9753eb86093STrond Myklebust 					&lo->plh_stateid)) {
9763eb86093STrond Myklebust 			/* Do we need to delay before resending? */
9773eb86093STrond Myklebust 			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
9783eb86093STrond Myklebust 						&data->args.errors[0].stateid))
9793eb86093STrond Myklebust 				rpc_delay(task, HZ);
9803eb86093STrond Myklebust 			rpc_restart_call_prepare(task);
9813eb86093STrond Myklebust 		}
9823eb86093STrond Myklebust 		spin_unlock(&inode->i_lock);
9833eb86093STrond Myklebust 		break;
9843eb86093STrond Myklebust 	case -ENOTSUPP:
9853eb86093STrond Myklebust 	case -EOPNOTSUPP:
9863eb86093STrond Myklebust 		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTERROR;
9873eb86093STrond Myklebust 	}
988638037b1STrond Myklebust 
989638037b1STrond Myklebust 	trace_nfs4_layouterror(inode, &data->args.errors[0].stateid,
990638037b1STrond Myklebust 			       task->tk_status);
9913eb86093STrond Myklebust }
9923eb86093STrond Myklebust 
9933eb86093STrond Myklebust static void
nfs42_layouterror_release(void * calldata)9943eb86093STrond Myklebust nfs42_layouterror_release(void *calldata)
9953eb86093STrond Myklebust {
9963eb86093STrond Myklebust 	struct nfs42_layouterror_data *data = calldata;
9973eb86093STrond Myklebust 
9983eb86093STrond Myklebust 	nfs42_free_layouterror_data(data);
9993eb86093STrond Myklebust }
10003eb86093STrond Myklebust 
10013eb86093STrond Myklebust static const struct rpc_call_ops nfs42_layouterror_ops = {
10023eb86093STrond Myklebust 	.rpc_call_prepare = nfs42_layouterror_prepare,
10033eb86093STrond Myklebust 	.rpc_call_done = nfs42_layouterror_done,
10043eb86093STrond Myklebust 	.rpc_release = nfs42_layouterror_release,
10053eb86093STrond Myklebust };
10063eb86093STrond Myklebust 
nfs42_proc_layouterror(struct pnfs_layout_segment * lseg,const struct nfs42_layout_error * errors,size_t n)10073eb86093STrond Myklebust int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
10083eb86093STrond Myklebust 		const struct nfs42_layout_error *errors, size_t n)
10093eb86093STrond Myklebust {
10103eb86093STrond Myklebust 	struct inode *inode = lseg->pls_layout->plh_inode;
10113eb86093STrond Myklebust 	struct nfs42_layouterror_data *data;
10123eb86093STrond Myklebust 	struct rpc_task *task;
10133eb86093STrond Myklebust 	struct rpc_message msg = {
10143eb86093STrond Myklebust 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTERROR],
10153eb86093STrond Myklebust 	};
10163eb86093STrond Myklebust 	struct rpc_task_setup task_setup = {
10173eb86093STrond Myklebust 		.rpc_message = &msg,
10183eb86093STrond Myklebust 		.callback_ops = &nfs42_layouterror_ops,
10193eb86093STrond Myklebust 		.flags = RPC_TASK_ASYNC,
10203eb86093STrond Myklebust 	};
10213eb86093STrond Myklebust 	unsigned int i;
10223eb86093STrond Myklebust 
10233eb86093STrond Myklebust 	if (!nfs_server_capable(inode, NFS_CAP_LAYOUTERROR))
10243eb86093STrond Myklebust 		return -EOPNOTSUPP;
10253eb86093STrond Myklebust 	if (n > NFS42_LAYOUTERROR_MAX)
10263eb86093STrond Myklebust 		return -EINVAL;
102763d8a41bSTrond Myklebust 	data = nfs42_alloc_layouterror_data(lseg, nfs_io_gfp_mask());
10283eb86093STrond Myklebust 	if (!data)
10293eb86093STrond Myklebust 		return -ENOMEM;
10303eb86093STrond Myklebust 	for (i = 0; i < n; i++) {
10313eb86093STrond Myklebust 		data->args.errors[i] = errors[i];
10323eb86093STrond Myklebust 		data->args.num_errors++;
10333eb86093STrond Myklebust 		data->res.num_errors++;
10343eb86093STrond Myklebust 	}
10353eb86093STrond Myklebust 	msg.rpc_argp = &data->args;
10363eb86093STrond Myklebust 	msg.rpc_resp = &data->res;
10373eb86093STrond Myklebust 	task_setup.callback_data = data;
10383eb86093STrond Myklebust 	task_setup.rpc_client = NFS_SERVER(inode)->client;
10393eb86093STrond Myklebust 	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
10403eb86093STrond Myklebust 	task = rpc_run_task(&task_setup);
10413eb86093STrond Myklebust 	if (IS_ERR(task))
10423eb86093STrond Myklebust 		return PTR_ERR(task);
10433eb86093STrond Myklebust 	rpc_put_task(task);
10443eb86093STrond Myklebust 	return 0;
10453eb86093STrond Myklebust }
10463eb86093STrond Myklebust EXPORT_SYMBOL_GPL(nfs42_proc_layouterror);
10473eb86093STrond Myklebust 
_nfs42_proc_clone(struct rpc_message * msg,struct file * src_f,struct file * dst_f,struct nfs_lock_context * src_lock,struct nfs_lock_context * dst_lock,loff_t src_offset,loff_t dst_offset,loff_t count)1048e5341f3aSPeng Tao static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
10494bdf87ebSChristoph Hellwig 		struct file *dst_f, struct nfs_lock_context *src_lock,
10504bdf87ebSChristoph Hellwig 		struct nfs_lock_context *dst_lock, loff_t src_offset,
1051e5341f3aSPeng Tao 		loff_t dst_offset, loff_t count)
1052e5341f3aSPeng Tao {
1053e5341f3aSPeng Tao 	struct inode *src_inode = file_inode(src_f);
1054e5341f3aSPeng Tao 	struct inode *dst_inode = file_inode(dst_f);
1055e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(dst_inode);
105634bf20ceSTrond Myklebust 	__u32 dst_bitmask[NFS_BITMASK_SZ];
1057e5341f3aSPeng Tao 	struct nfs42_clone_args args = {
1058e5341f3aSPeng Tao 		.src_fh = NFS_FH(src_inode),
1059e5341f3aSPeng Tao 		.dst_fh = NFS_FH(dst_inode),
1060e5341f3aSPeng Tao 		.src_offset = src_offset,
1061e5341f3aSPeng Tao 		.dst_offset = dst_offset,
10629494b2ceSChristoph Hellwig 		.count = count,
106334bf20ceSTrond Myklebust 		.dst_bitmask = dst_bitmask,
1064e5341f3aSPeng Tao 	};
1065e5341f3aSPeng Tao 	struct nfs42_clone_res res = {
1066e5341f3aSPeng Tao 		.server	= server,
1067e5341f3aSPeng Tao 	};
1068e5341f3aSPeng Tao 	int status;
1069e5341f3aSPeng Tao 
1070e5341f3aSPeng Tao 	msg->rpc_argp = &args;
1071e5341f3aSPeng Tao 	msg->rpc_resp = &res;
1072e5341f3aSPeng Tao 
10734bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context,
10744bdf87ebSChristoph Hellwig 			src_lock, FMODE_READ);
1075d826e5b8SOlga Kornievskaia 	if (status) {
1076d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
1077d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
1078e5341f3aSPeng Tao 		return status;
1079d826e5b8SOlga Kornievskaia 	}
10804bdf87ebSChristoph Hellwig 	status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
10814bdf87ebSChristoph Hellwig 			dst_lock, FMODE_WRITE);
1082d826e5b8SOlga Kornievskaia 	if (status) {
1083d826e5b8SOlga Kornievskaia 		if (status == -EAGAIN)
1084d826e5b8SOlga Kornievskaia 			status = -NFS4ERR_BAD_STATEID;
1085e5341f3aSPeng Tao 		return status;
1086d826e5b8SOlga Kornievskaia 	}
1087e5341f3aSPeng Tao 
1088e5341f3aSPeng Tao 	res.dst_fattr = nfs_alloc_fattr();
1089e5341f3aSPeng Tao 	if (!res.dst_fattr)
1090e5341f3aSPeng Tao 		return -ENOMEM;
1091e5341f3aSPeng Tao 
109234bf20ceSTrond Myklebust 	nfs4_bitmask_set(dst_bitmask, server->cache_consistency_bitmask,
109334bf20ceSTrond Myklebust 			 dst_inode, NFS_INO_INVALID_BLOCKS);
109434bf20ceSTrond Myklebust 
1095e5341f3aSPeng Tao 	status = nfs4_call_sync(server->client, server, msg,
1096e5341f3aSPeng Tao 				&args.seq_args, &res.seq_res, 0);
10972a65ca8bSOlga Kornievskaia 	trace_nfs4_clone(src_inode, dst_inode, &args, status);
109894d202d5STrond Myklebust 	if (status == 0) {
1099038efb63SBenjamin Coddington 		/* a zero-length count means clone to EOF in src */
1100038efb63SBenjamin Coddington 		if (count == 0 && res.dst_fattr->valid & NFS_ATTR_FATTR_SIZE)
1101038efb63SBenjamin Coddington 			count = nfs_size_to_loff_t(res.dst_fattr->size) - dst_offset;
110294d202d5STrond Myklebust 		nfs42_copy_dest_done(dst_inode, dst_offset, count);
1103e5341f3aSPeng Tao 		status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
110494d202d5STrond Myklebust 	}
1105e5341f3aSPeng Tao 
1106e5341f3aSPeng Tao 	kfree(res.dst_fattr);
1107e5341f3aSPeng Tao 	return status;
1108e5341f3aSPeng Tao }
1109e5341f3aSPeng Tao 
nfs42_proc_clone(struct file * src_f,struct file * dst_f,loff_t src_offset,loff_t dst_offset,loff_t count)1110e5341f3aSPeng Tao int nfs42_proc_clone(struct file *src_f, struct file *dst_f,
1111e5341f3aSPeng Tao 		     loff_t src_offset, loff_t dst_offset, loff_t count)
1112e5341f3aSPeng Tao {
1113e5341f3aSPeng Tao 	struct rpc_message msg = {
1114e5341f3aSPeng Tao 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE],
1115e5341f3aSPeng Tao 	};
1116e5341f3aSPeng Tao 	struct inode *inode = file_inode(src_f);
1117e5341f3aSPeng Tao 	struct nfs_server *server = NFS_SERVER(file_inode(src_f));
11184bdf87ebSChristoph Hellwig 	struct nfs_lock_context *src_lock;
11194bdf87ebSChristoph Hellwig 	struct nfs_lock_context *dst_lock;
11204bdf87ebSChristoph Hellwig 	struct nfs4_exception src_exception = { };
11214bdf87ebSChristoph Hellwig 	struct nfs4_exception dst_exception = { };
11224bdf87ebSChristoph Hellwig 	int err, err2;
1123e5341f3aSPeng Tao 
1124e5341f3aSPeng Tao 	if (!nfs_server_capable(inode, NFS_CAP_CLONE))
1125e5341f3aSPeng Tao 		return -EOPNOTSUPP;
1126e5341f3aSPeng Tao 
11274bdf87ebSChristoph Hellwig 	src_lock = nfs_get_lock_context(nfs_file_open_context(src_f));
11284bdf87ebSChristoph Hellwig 	if (IS_ERR(src_lock))
11294bdf87ebSChristoph Hellwig 		return PTR_ERR(src_lock);
11304bdf87ebSChristoph Hellwig 
11314bdf87ebSChristoph Hellwig 	src_exception.inode = file_inode(src_f);
11324bdf87ebSChristoph Hellwig 	src_exception.state = src_lock->open_context->state;
11334bdf87ebSChristoph Hellwig 
11344bdf87ebSChristoph Hellwig 	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f));
11354bdf87ebSChristoph Hellwig 	if (IS_ERR(dst_lock)) {
11364bdf87ebSChristoph Hellwig 		err = PTR_ERR(dst_lock);
11374bdf87ebSChristoph Hellwig 		goto out_put_src_lock;
11384bdf87ebSChristoph Hellwig 	}
11394bdf87ebSChristoph Hellwig 
11404bdf87ebSChristoph Hellwig 	dst_exception.inode = file_inode(dst_f);
11414bdf87ebSChristoph Hellwig 	dst_exception.state = dst_lock->open_context->state;
11424bdf87ebSChristoph Hellwig 
1143e5341f3aSPeng Tao 	do {
11444bdf87ebSChristoph Hellwig 		err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock,
11454bdf87ebSChristoph Hellwig 					src_offset, dst_offset, count);
1146e5341f3aSPeng Tao 		if (err == -ENOTSUPP || err == -EOPNOTSUPP) {
1147e5341f3aSPeng Tao 			NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE;
11484bdf87ebSChristoph Hellwig 			err = -EOPNOTSUPP;
11494bdf87ebSChristoph Hellwig 			break;
1150e5341f3aSPeng Tao 		}
1151e5341f3aSPeng Tao 
11524bdf87ebSChristoph Hellwig 		err2 = nfs4_handle_exception(server, err, &src_exception);
11534bdf87ebSChristoph Hellwig 		err = nfs4_handle_exception(server, err, &dst_exception);
11544bdf87ebSChristoph Hellwig 		if (!err)
11554bdf87ebSChristoph Hellwig 			err = err2;
11564bdf87ebSChristoph Hellwig 	} while (src_exception.retry || dst_exception.retry);
11574bdf87ebSChristoph Hellwig 
11584bdf87ebSChristoph Hellwig 	nfs_put_lock_context(dst_lock);
11594bdf87ebSChristoph Hellwig out_put_src_lock:
11604bdf87ebSChristoph Hellwig 	nfs_put_lock_context(src_lock);
1161e5341f3aSPeng Tao 	return err;
1162e5341f3aSPeng Tao }
1163c10a7514SFrank van der Linden 
1164c10a7514SFrank van der Linden #define NFS4XATTR_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
1165c10a7514SFrank van der Linden 
_nfs42_proc_removexattr(struct inode * inode,const char * name)1166c10a7514SFrank van der Linden static int _nfs42_proc_removexattr(struct inode *inode, const char *name)
1167c10a7514SFrank van der Linden {
1168c10a7514SFrank van der Linden 	struct nfs_server *server = NFS_SERVER(inode);
1169c10a7514SFrank van der Linden 	struct nfs42_removexattrargs args = {
1170c10a7514SFrank van der Linden 		.fh = NFS_FH(inode),
1171c10a7514SFrank van der Linden 		.xattr_name = name,
1172c10a7514SFrank van der Linden 	};
1173c10a7514SFrank van der Linden 	struct nfs42_removexattrres res;
1174c10a7514SFrank van der Linden 	struct rpc_message msg = {
1175c10a7514SFrank van der Linden 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVEXATTR],
1176c10a7514SFrank van der Linden 		.rpc_argp = &args,
1177c10a7514SFrank van der Linden 		.rpc_resp = &res,
1178c10a7514SFrank van der Linden 	};
1179c10a7514SFrank van der Linden 	int ret;
1180c10a7514SFrank van der Linden 	unsigned long timestamp = jiffies;
1181c10a7514SFrank van der Linden 
1182c10a7514SFrank van der Linden 	ret = nfs4_call_sync(server->client, server, &msg, &args.seq_args,
1183c10a7514SFrank van der Linden 	    &res.seq_res, 1);
118427ffed10SAnna Schumaker 	trace_nfs4_removexattr(inode, name, ret);
1185c10a7514SFrank van der Linden 	if (!ret)
1186c10a7514SFrank van der Linden 		nfs4_update_changeattr(inode, &res.cinfo, timestamp, 0);
1187c10a7514SFrank van der Linden 
1188c10a7514SFrank van der Linden 	return ret;
1189c10a7514SFrank van der Linden }
1190c10a7514SFrank van der Linden 
_nfs42_proc_setxattr(struct inode * inode,const char * name,const void * buf,size_t buflen,int flags)1191c10a7514SFrank van der Linden static int _nfs42_proc_setxattr(struct inode *inode, const char *name,
1192c10a7514SFrank van der Linden 				const void *buf, size_t buflen, int flags)
1193c10a7514SFrank van der Linden {
1194c10a7514SFrank van der Linden 	struct nfs_server *server = NFS_SERVER(inode);
119586e2e1f6SAnna Schumaker 	__u32 bitmask[NFS_BITMASK_SZ];
1196c10a7514SFrank van der Linden 	struct page *pages[NFS4XATTR_MAXPAGES];
1197c10a7514SFrank van der Linden 	struct nfs42_setxattrargs arg = {
1198c10a7514SFrank van der Linden 		.fh		= NFS_FH(inode),
119986e2e1f6SAnna Schumaker 		.bitmask	= bitmask,
1200c10a7514SFrank van der Linden 		.xattr_pages	= pages,
1201c10a7514SFrank van der Linden 		.xattr_len	= buflen,
1202c10a7514SFrank van der Linden 		.xattr_name	= name,
1203c10a7514SFrank van der Linden 		.xattr_flags	= flags,
1204c10a7514SFrank van der Linden 	};
120586e2e1f6SAnna Schumaker 	struct nfs42_setxattrres res = {
120686e2e1f6SAnna Schumaker 		.server		= server,
120786e2e1f6SAnna Schumaker 	};
1208c10a7514SFrank van der Linden 	struct rpc_message msg = {
1209c10a7514SFrank van der Linden 		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_SETXATTR],
1210c10a7514SFrank van der Linden 		.rpc_argp	= &arg,
1211c10a7514SFrank van der Linden 		.rpc_resp	= &res,
1212c10a7514SFrank van der Linden 	};
1213c10a7514SFrank van der Linden 	int ret, np;
1214c10a7514SFrank van der Linden 	unsigned long timestamp = jiffies;
1215c10a7514SFrank van der Linden 
1216c10a7514SFrank van der Linden 	if (buflen > server->sxasize)
1217c10a7514SFrank van der Linden 		return -ERANGE;
1218c10a7514SFrank van der Linden 
121986e2e1f6SAnna Schumaker 	res.fattr = nfs_alloc_fattr();
122086e2e1f6SAnna Schumaker 	if (!res.fattr)
122186e2e1f6SAnna Schumaker 		return -ENOMEM;
122286e2e1f6SAnna Schumaker 
1223c10a7514SFrank van der Linden 	if (buflen > 0) {
1224c10a7514SFrank van der Linden 		np = nfs4_buf_to_pages_noslab(buf, buflen, arg.xattr_pages);
122586e2e1f6SAnna Schumaker 		if (np < 0) {
122686e2e1f6SAnna Schumaker 			ret = np;
122786e2e1f6SAnna Schumaker 			goto out;
122886e2e1f6SAnna Schumaker 		}
1229c10a7514SFrank van der Linden 	} else
1230c10a7514SFrank van der Linden 		np = 0;
1231c10a7514SFrank van der Linden 
123286e2e1f6SAnna Schumaker 	nfs4_bitmask_set(bitmask, server->cache_consistency_bitmask,
123386e2e1f6SAnna Schumaker 			 inode, NFS_INO_INVALID_CHANGE);
123486e2e1f6SAnna Schumaker 
1235c10a7514SFrank van der Linden 	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
1236c10a7514SFrank van der Linden 	    &res.seq_res, 1);
123727ffed10SAnna Schumaker 	trace_nfs4_setxattr(inode, name, ret);
1238c10a7514SFrank van der Linden 
1239c10a7514SFrank van der Linden 	for (; np > 0; np--)
1240c10a7514SFrank van der Linden 		put_page(pages[np - 1]);
1241c10a7514SFrank van der Linden 
124286e2e1f6SAnna Schumaker 	if (!ret) {
1243c10a7514SFrank van der Linden 		nfs4_update_changeattr(inode, &res.cinfo, timestamp, 0);
124486e2e1f6SAnna Schumaker 		ret = nfs_post_op_update_inode(inode, res.fattr);
124586e2e1f6SAnna Schumaker 	}
1246c10a7514SFrank van der Linden 
124786e2e1f6SAnna Schumaker out:
124886e2e1f6SAnna Schumaker 	kfree(res.fattr);
1249c10a7514SFrank van der Linden 	return ret;
1250c10a7514SFrank van der Linden }
1251c10a7514SFrank van der Linden 
_nfs42_proc_getxattr(struct inode * inode,const char * name,void * buf,size_t buflen,struct page ** pages,size_t plen)1252c10a7514SFrank van der Linden static ssize_t _nfs42_proc_getxattr(struct inode *inode, const char *name,
1253a1f26739SFrank van der Linden 				void *buf, size_t buflen, struct page **pages,
1254a1f26739SFrank van der Linden 				size_t plen)
1255c10a7514SFrank van der Linden {
1256c10a7514SFrank van der Linden 	struct nfs_server *server = NFS_SERVER(inode);
1257c10a7514SFrank van der Linden 	struct nfs42_getxattrargs arg = {
1258c10a7514SFrank van der Linden 		.fh		= NFS_FH(inode),
1259c10a7514SFrank van der Linden 		.xattr_name	= name,
1260c10a7514SFrank van der Linden 	};
1261c10a7514SFrank van der Linden 	struct nfs42_getxattrres res;
1262c10a7514SFrank van der Linden 	struct rpc_message msg = {
1263c10a7514SFrank van der Linden 		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_GETXATTR],
1264c10a7514SFrank van der Linden 		.rpc_argp	= &arg,
1265c10a7514SFrank van der Linden 		.rpc_resp	= &res,
1266c10a7514SFrank van der Linden 	};
1267a1f26739SFrank van der Linden 	ssize_t ret;
1268a1f26739SFrank van der Linden 
1269a1f26739SFrank van der Linden 	arg.xattr_len = plen;
1270a1f26739SFrank van der Linden 	arg.xattr_pages = pages;
1271c10a7514SFrank van der Linden 
1272c10a7514SFrank van der Linden 	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
1273c10a7514SFrank van der Linden 	    &res.seq_res, 0);
127427ffed10SAnna Schumaker 	trace_nfs4_getxattr(inode, name, ret);
1275c10a7514SFrank van der Linden 	if (ret < 0)
1276c10a7514SFrank van der Linden 		return ret;
1277c10a7514SFrank van der Linden 
127895ad37f9SFrank van der Linden 	/*
127995ad37f9SFrank van der Linden 	 * Normally, the caching is done one layer up, but for successful
128095ad37f9SFrank van der Linden 	 * RPCS, always cache the result here, even if the caller was
128195ad37f9SFrank van der Linden 	 * just querying the length, or if the reply was too big for
128295ad37f9SFrank van der Linden 	 * the caller. This avoids a second RPC in the case of the
128395ad37f9SFrank van der Linden 	 * common query-alloc-retrieve cycle for xattrs.
128495ad37f9SFrank van der Linden 	 *
128595ad37f9SFrank van der Linden 	 * Note that xattr_len is always capped to XATTR_SIZE_MAX.
128695ad37f9SFrank van der Linden 	 */
128795ad37f9SFrank van der Linden 
128895ad37f9SFrank van der Linden 	nfs4_xattr_cache_add(inode, name, NULL, pages, res.xattr_len);
128995ad37f9SFrank van der Linden 
1290c10a7514SFrank van der Linden 	if (buflen) {
1291c10a7514SFrank van der Linden 		if (res.xattr_len > buflen)
1292c10a7514SFrank van der Linden 			return -ERANGE;
1293c10a7514SFrank van der Linden 		_copy_from_pages(buf, pages, 0, res.xattr_len);
1294c10a7514SFrank van der Linden 	}
1295c10a7514SFrank van der Linden 
1296c10a7514SFrank van der Linden 	return res.xattr_len;
1297c10a7514SFrank van der Linden }
1298c10a7514SFrank van der Linden 
_nfs42_proc_listxattrs(struct inode * inode,void * buf,size_t buflen,u64 * cookiep,bool * eofp)1299c10a7514SFrank van der Linden static ssize_t _nfs42_proc_listxattrs(struct inode *inode, void *buf,
1300c10a7514SFrank van der Linden 				 size_t buflen, u64 *cookiep, bool *eofp)
1301c10a7514SFrank van der Linden {
1302c10a7514SFrank van der Linden 	struct nfs_server *server = NFS_SERVER(inode);
1303c10a7514SFrank van der Linden 	struct page **pages;
1304c10a7514SFrank van der Linden 	struct nfs42_listxattrsargs arg = {
1305c10a7514SFrank van der Linden 		.fh		= NFS_FH(inode),
1306c10a7514SFrank van der Linden 		.cookie		= *cookiep,
1307c10a7514SFrank van der Linden 	};
1308c10a7514SFrank van der Linden 	struct nfs42_listxattrsres res = {
1309c10a7514SFrank van der Linden 		.eof = false,
1310c10a7514SFrank van der Linden 		.xattr_buf = buf,
1311c10a7514SFrank van der Linden 		.xattr_len = buflen,
1312c10a7514SFrank van der Linden 	};
1313c10a7514SFrank van der Linden 	struct rpc_message msg = {
1314c10a7514SFrank van der Linden 		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_LISTXATTRS],
1315c10a7514SFrank van der Linden 		.rpc_argp	= &arg,
1316c10a7514SFrank van der Linden 		.rpc_resp	= &res,
1317c10a7514SFrank van der Linden 	};
1318c10a7514SFrank van der Linden 	u32 xdrlen;
13195482e09aSChuck Lever 	int ret, np, i;
1320c10a7514SFrank van der Linden 
1321c10a7514SFrank van der Linden 
13225482e09aSChuck Lever 	ret = -ENOMEM;
1323c10a7514SFrank van der Linden 	res.scratch = alloc_page(GFP_KERNEL);
1324c10a7514SFrank van der Linden 	if (!res.scratch)
13255482e09aSChuck Lever 		goto out;
1326c10a7514SFrank van der Linden 
1327c10a7514SFrank van der Linden 	xdrlen = nfs42_listxattr_xdrsize(buflen);
1328c10a7514SFrank van der Linden 	if (xdrlen > server->lxasize)
1329c10a7514SFrank van der Linden 		xdrlen = server->lxasize;
1330c10a7514SFrank van der Linden 	np = xdrlen / PAGE_SIZE + 1;
1331c10a7514SFrank van der Linden 
1332c10a7514SFrank van der Linden 	pages = kcalloc(np, sizeof(struct page *), GFP_KERNEL);
13335482e09aSChuck Lever 	if (!pages)
13345482e09aSChuck Lever 		goto out_free_scratch;
13355482e09aSChuck Lever 	for (i = 0; i < np; i++) {
13365482e09aSChuck Lever 		pages[i] = alloc_page(GFP_KERNEL);
13375482e09aSChuck Lever 		if (!pages[i])
13385482e09aSChuck Lever 			goto out_free_pages;
1339c10a7514SFrank van der Linden 	}
1340c10a7514SFrank van der Linden 
1341c10a7514SFrank van der Linden 	arg.xattr_pages = pages;
1342c10a7514SFrank van der Linden 	arg.count = xdrlen;
1343c10a7514SFrank van der Linden 
1344c10a7514SFrank van der Linden 	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
1345c10a7514SFrank van der Linden 	    &res.seq_res, 0);
1346a0b685e7SAnna Schumaker 	trace_nfs4_listxattr(inode, ret);
1347c10a7514SFrank van der Linden 
1348c10a7514SFrank van der Linden 	if (ret >= 0) {
1349c10a7514SFrank van der Linden 		ret = res.copied;
1350c10a7514SFrank van der Linden 		*cookiep = res.cookie;
1351c10a7514SFrank van der Linden 		*eofp = res.eof;
1352c10a7514SFrank van der Linden 	}
1353c10a7514SFrank van der Linden 
13545482e09aSChuck Lever out_free_pages:
1355c10a7514SFrank van der Linden 	while (--np >= 0) {
1356c10a7514SFrank van der Linden 		if (pages[np])
1357c10a7514SFrank van der Linden 			__free_page(pages[np]);
1358c10a7514SFrank van der Linden 	}
1359c10a7514SFrank van der Linden 	kfree(pages);
13605482e09aSChuck Lever out_free_scratch:
13615482e09aSChuck Lever 	__free_page(res.scratch);
13625482e09aSChuck Lever out:
1363c10a7514SFrank van der Linden 	return ret;
1364c10a7514SFrank van der Linden 
1365c10a7514SFrank van der Linden }
1366c10a7514SFrank van der Linden 
nfs42_proc_getxattr(struct inode * inode,const char * name,void * buf,size_t buflen)1367c10a7514SFrank van der Linden ssize_t nfs42_proc_getxattr(struct inode *inode, const char *name,
1368c10a7514SFrank van der Linden 			      void *buf, size_t buflen)
1369c10a7514SFrank van der Linden {
1370c10a7514SFrank van der Linden 	struct nfs4_exception exception = { };
1371a1f26739SFrank van der Linden 	ssize_t err, np, i;
1372a1f26739SFrank van der Linden 	struct page **pages;
1373c10a7514SFrank van der Linden 
1374a1f26739SFrank van der Linden 	np = nfs_page_array_len(0, buflen ?: XATTR_SIZE_MAX);
1375a1f26739SFrank van der Linden 	pages = kmalloc_array(np, sizeof(*pages), GFP_KERNEL);
1376a1f26739SFrank van der Linden 	if (!pages)
1377a1f26739SFrank van der Linden 		return -ENOMEM;
1378a1f26739SFrank van der Linden 
1379a1f26739SFrank van der Linden 	for (i = 0; i < np; i++) {
1380a1f26739SFrank van der Linden 		pages[i] = alloc_page(GFP_KERNEL);
1381a1f26739SFrank van der Linden 		if (!pages[i]) {
13827be9b38aSColin Ian King 			err = -ENOMEM;
1383a1f26739SFrank van der Linden 			goto out;
1384a1f26739SFrank van der Linden 		}
1385a1f26739SFrank van der Linden 	}
1386a1f26739SFrank van der Linden 
1387a1f26739SFrank van der Linden 	/*
1388a1f26739SFrank van der Linden 	 * The GETXATTR op has no length field in the call, and the
1389a1f26739SFrank van der Linden 	 * xattr data is at the end of the reply.
1390a1f26739SFrank van der Linden 	 *
1391a1f26739SFrank van der Linden 	 * There is no downside in using the page-aligned length. It will
1392a1f26739SFrank van der Linden 	 * allow receiving and caching xattrs that are too large for the
1393a1f26739SFrank van der Linden 	 * caller but still fit in the page-rounded value.
1394a1f26739SFrank van der Linden 	 */
1395c10a7514SFrank van der Linden 	do {
1396a1f26739SFrank van der Linden 		err = _nfs42_proc_getxattr(inode, name, buf, buflen,
1397a1f26739SFrank van der Linden 			pages, np * PAGE_SIZE);
1398c10a7514SFrank van der Linden 		if (err >= 0)
1399c10a7514SFrank van der Linden 			break;
1400c10a7514SFrank van der Linden 		err = nfs4_handle_exception(NFS_SERVER(inode), err,
1401c10a7514SFrank van der Linden 				&exception);
1402c10a7514SFrank van der Linden 	} while (exception.retry);
1403c10a7514SFrank van der Linden 
1404a1f26739SFrank van der Linden out:
14054e3733fdSFedor Pchelkin 	while (--i >= 0)
14064e3733fdSFedor Pchelkin 		__free_page(pages[i]);
1407a1f26739SFrank van der Linden 	kfree(pages);
1408a1f26739SFrank van der Linden 
1409c10a7514SFrank van der Linden 	return err;
1410c10a7514SFrank van der Linden }
1411c10a7514SFrank van der Linden 
nfs42_proc_setxattr(struct inode * inode,const char * name,const void * buf,size_t buflen,int flags)1412c10a7514SFrank van der Linden int nfs42_proc_setxattr(struct inode *inode, const char *name,
1413c10a7514SFrank van der Linden 			      const void *buf, size_t buflen, int flags)
1414c10a7514SFrank van der Linden {
1415c10a7514SFrank van der Linden 	struct nfs4_exception exception = { };
1416c10a7514SFrank van der Linden 	int err;
1417c10a7514SFrank van der Linden 
1418c10a7514SFrank van der Linden 	do {
1419c10a7514SFrank van der Linden 		err = _nfs42_proc_setxattr(inode, name, buf, buflen, flags);
1420c10a7514SFrank van der Linden 		if (!err)
1421c10a7514SFrank van der Linden 			break;
1422c10a7514SFrank van der Linden 		err = nfs4_handle_exception(NFS_SERVER(inode), err,
1423c10a7514SFrank van der Linden 				&exception);
1424c10a7514SFrank van der Linden 	} while (exception.retry);
1425c10a7514SFrank van der Linden 
1426c10a7514SFrank van der Linden 	return err;
1427c10a7514SFrank van der Linden }
1428c10a7514SFrank van der Linden 
nfs42_proc_listxattrs(struct inode * inode,void * buf,size_t buflen,u64 * cookiep,bool * eofp)1429c10a7514SFrank van der Linden ssize_t nfs42_proc_listxattrs(struct inode *inode, void *buf,
1430c10a7514SFrank van der Linden 			      size_t buflen, u64 *cookiep, bool *eofp)
1431c10a7514SFrank van der Linden {
1432c10a7514SFrank van der Linden 	struct nfs4_exception exception = { };
1433c10a7514SFrank van der Linden 	ssize_t err;
1434c10a7514SFrank van der Linden 
1435c10a7514SFrank van der Linden 	do {
1436c10a7514SFrank van der Linden 		err = _nfs42_proc_listxattrs(inode, buf, buflen,
1437c10a7514SFrank van der Linden 		    cookiep, eofp);
1438c10a7514SFrank van der Linden 		if (err >= 0)
1439c10a7514SFrank van der Linden 			break;
1440c10a7514SFrank van der Linden 		err = nfs4_handle_exception(NFS_SERVER(inode), err,
1441c10a7514SFrank van der Linden 				&exception);
1442c10a7514SFrank van der Linden 	} while (exception.retry);
1443c10a7514SFrank van der Linden 
1444c10a7514SFrank van der Linden 	return err;
1445c10a7514SFrank van der Linden }
1446c10a7514SFrank van der Linden 
nfs42_proc_removexattr(struct inode * inode,const char * name)1447c10a7514SFrank van der Linden int nfs42_proc_removexattr(struct inode *inode, const char *name)
1448c10a7514SFrank van der Linden {
1449c10a7514SFrank van der Linden 	struct nfs4_exception exception = { };
1450c10a7514SFrank van der Linden 	int err;
1451c10a7514SFrank van der Linden 
1452c10a7514SFrank van der Linden 	do {
1453c10a7514SFrank van der Linden 		err = _nfs42_proc_removexattr(inode, name);
1454c10a7514SFrank van der Linden 		if (!err)
1455c10a7514SFrank van der Linden 			break;
1456c10a7514SFrank van der Linden 		err = nfs4_handle_exception(NFS_SERVER(inode), err,
1457c10a7514SFrank van der Linden 				&exception);
1458c10a7514SFrank van der Linden 	} while (exception.retry);
1459c10a7514SFrank van der Linden 
1460c10a7514SFrank van der Linden 	return err;
1461c10a7514SFrank van der Linden }
1462