162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2014 Anna Schumaker <Anna.Schumaker@Netapp.com>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/fs.h>
662306a36Sopenharmony_ci#include <linux/sunrpc/addr.h>
762306a36Sopenharmony_ci#include <linux/sunrpc/sched.h>
862306a36Sopenharmony_ci#include <linux/nfs.h>
962306a36Sopenharmony_ci#include <linux/nfs3.h>
1062306a36Sopenharmony_ci#include <linux/nfs4.h>
1162306a36Sopenharmony_ci#include <linux/nfs_xdr.h>
1262306a36Sopenharmony_ci#include <linux/nfs_fs.h>
1362306a36Sopenharmony_ci#include "nfs4_fs.h"
1462306a36Sopenharmony_ci#include "nfs42.h"
1562306a36Sopenharmony_ci#include "iostat.h"
1662306a36Sopenharmony_ci#include "pnfs.h"
1762306a36Sopenharmony_ci#include "nfs4session.h"
1862306a36Sopenharmony_ci#include "internal.h"
1962306a36Sopenharmony_ci#include "delegation.h"
2062306a36Sopenharmony_ci#include "nfs4trace.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_PROC
2362306a36Sopenharmony_cistatic int nfs42_do_offload_cancel_async(struct file *dst, nfs4_stateid *std);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void nfs42_set_netaddr(struct file *filep, struct nfs42_netaddr *naddr)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct nfs_client *clp = (NFS_SERVER(file_inode(filep)))->nfs_client;
2862306a36Sopenharmony_ci	unsigned short port = 2049;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	rcu_read_lock();
3162306a36Sopenharmony_ci	naddr->netid_len = scnprintf(naddr->netid,
3262306a36Sopenharmony_ci					sizeof(naddr->netid), "%s",
3362306a36Sopenharmony_ci					rpc_peeraddr2str(clp->cl_rpcclient,
3462306a36Sopenharmony_ci					RPC_DISPLAY_NETID));
3562306a36Sopenharmony_ci	naddr->addr_len = scnprintf(naddr->addr,
3662306a36Sopenharmony_ci					sizeof(naddr->addr),
3762306a36Sopenharmony_ci					"%s.%u.%u",
3862306a36Sopenharmony_ci					rpc_peeraddr2str(clp->cl_rpcclient,
3962306a36Sopenharmony_ci					RPC_DISPLAY_ADDR),
4062306a36Sopenharmony_ci					port >> 8, port & 255);
4162306a36Sopenharmony_ci	rcu_read_unlock();
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
4562306a36Sopenharmony_ci		struct nfs_lock_context *lock, loff_t offset, loff_t len)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct inode *inode = file_inode(filep);
4862306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
4962306a36Sopenharmony_ci	u32 bitmask[NFS_BITMASK_SZ];
5062306a36Sopenharmony_ci	struct nfs42_falloc_args args = {
5162306a36Sopenharmony_ci		.falloc_fh	= NFS_FH(inode),
5262306a36Sopenharmony_ci		.falloc_offset	= offset,
5362306a36Sopenharmony_ci		.falloc_length	= len,
5462306a36Sopenharmony_ci		.falloc_bitmask	= bitmask,
5562306a36Sopenharmony_ci	};
5662306a36Sopenharmony_ci	struct nfs42_falloc_res res = {
5762306a36Sopenharmony_ci		.falloc_server	= server,
5862306a36Sopenharmony_ci	};
5962306a36Sopenharmony_ci	int status;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	msg->rpc_argp = &args;
6262306a36Sopenharmony_ci	msg->rpc_resp = &res;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	status = nfs4_set_rw_stateid(&args.falloc_stateid, lock->open_context,
6562306a36Sopenharmony_ci			lock, FMODE_WRITE);
6662306a36Sopenharmony_ci	if (status) {
6762306a36Sopenharmony_ci		if (status == -EAGAIN)
6862306a36Sopenharmony_ci			status = -NFS4ERR_BAD_STATEID;
6962306a36Sopenharmony_ci		return status;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	nfs4_bitmask_set(bitmask, server->cache_consistency_bitmask, inode,
7362306a36Sopenharmony_ci			 NFS_INO_INVALID_BLOCKS);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	res.falloc_fattr = nfs_alloc_fattr();
7662306a36Sopenharmony_ci	if (!res.falloc_fattr)
7762306a36Sopenharmony_ci		return -ENOMEM;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	status = nfs4_call_sync(server->client, server, msg,
8062306a36Sopenharmony_ci				&args.seq_args, &res.seq_res, 0);
8162306a36Sopenharmony_ci	if (status == 0) {
8262306a36Sopenharmony_ci		if (nfs_should_remove_suid(inode)) {
8362306a36Sopenharmony_ci			spin_lock(&inode->i_lock);
8462306a36Sopenharmony_ci			nfs_set_cache_invalid(inode,
8562306a36Sopenharmony_ci				NFS_INO_REVAL_FORCED | NFS_INO_INVALID_MODE);
8662306a36Sopenharmony_ci			spin_unlock(&inode->i_lock);
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci		status = nfs_post_op_update_inode_force_wcc(inode,
8962306a36Sopenharmony_ci							    res.falloc_fattr);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	if (msg->rpc_proc == &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE])
9262306a36Sopenharmony_ci		trace_nfs4_fallocate(inode, &args, status);
9362306a36Sopenharmony_ci	else
9462306a36Sopenharmony_ci		trace_nfs4_deallocate(inode, &args, status);
9562306a36Sopenharmony_ci	kfree(res.falloc_fattr);
9662306a36Sopenharmony_ci	return status;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
10062306a36Sopenharmony_ci				loff_t offset, loff_t len)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct inode *inode = file_inode(filep);
10362306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
10462306a36Sopenharmony_ci	struct nfs4_exception exception = { };
10562306a36Sopenharmony_ci	struct nfs_lock_context *lock;
10662306a36Sopenharmony_ci	int err;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	lock = nfs_get_lock_context(nfs_file_open_context(filep));
10962306a36Sopenharmony_ci	if (IS_ERR(lock))
11062306a36Sopenharmony_ci		return PTR_ERR(lock);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	exception.inode = inode;
11362306a36Sopenharmony_ci	exception.state = lock->open_context->state;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	err = nfs_sync_inode(inode);
11662306a36Sopenharmony_ci	if (err)
11762306a36Sopenharmony_ci		goto out;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	do {
12062306a36Sopenharmony_ci		err = _nfs42_proc_fallocate(msg, filep, lock, offset, len);
12162306a36Sopenharmony_ci		if (err == -ENOTSUPP) {
12262306a36Sopenharmony_ci			err = -EOPNOTSUPP;
12362306a36Sopenharmony_ci			break;
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci		err = nfs4_handle_exception(server, err, &exception);
12662306a36Sopenharmony_ci	} while (exception.retry);
12762306a36Sopenharmony_ciout:
12862306a36Sopenharmony_ci	nfs_put_lock_context(lock);
12962306a36Sopenharmony_ci	return err;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ciint nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct rpc_message msg = {
13562306a36Sopenharmony_ci		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE],
13662306a36Sopenharmony_ci	};
13762306a36Sopenharmony_ci	struct inode *inode = file_inode(filep);
13862306a36Sopenharmony_ci	int err;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE))
14162306a36Sopenharmony_ci		return -EOPNOTSUPP;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	inode_lock(inode);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	err = nfs42_proc_fallocate(&msg, filep, offset, len);
14662306a36Sopenharmony_ci	if (err == -EOPNOTSUPP)
14762306a36Sopenharmony_ci		NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	inode_unlock(inode);
15062306a36Sopenharmony_ci	return err;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ciint nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct rpc_message msg = {
15662306a36Sopenharmony_ci		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE],
15762306a36Sopenharmony_ci	};
15862306a36Sopenharmony_ci	struct inode *inode = file_inode(filep);
15962306a36Sopenharmony_ci	int err;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE))
16262306a36Sopenharmony_ci		return -EOPNOTSUPP;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	inode_lock(inode);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	err = nfs42_proc_fallocate(&msg, filep, offset, len);
16762306a36Sopenharmony_ci	if (err == 0)
16862306a36Sopenharmony_ci		truncate_pagecache_range(inode, offset, (offset + len) -1);
16962306a36Sopenharmony_ci	if (err == -EOPNOTSUPP)
17062306a36Sopenharmony_ci		NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	inode_unlock(inode);
17362306a36Sopenharmony_ci	return err;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int handle_async_copy(struct nfs42_copy_res *res,
17762306a36Sopenharmony_ci			     struct nfs_server *dst_server,
17862306a36Sopenharmony_ci			     struct nfs_server *src_server,
17962306a36Sopenharmony_ci			     struct file *src,
18062306a36Sopenharmony_ci			     struct file *dst,
18162306a36Sopenharmony_ci			     nfs4_stateid *src_stateid,
18262306a36Sopenharmony_ci			     bool *restart)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct nfs4_copy_state *copy, *tmp_copy = NULL, *iter;
18562306a36Sopenharmony_ci	int status = NFS4_OK;
18662306a36Sopenharmony_ci	struct nfs_open_context *dst_ctx = nfs_file_open_context(dst);
18762306a36Sopenharmony_ci	struct nfs_open_context *src_ctx = nfs_file_open_context(src);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_KERNEL);
19062306a36Sopenharmony_ci	if (!copy)
19162306a36Sopenharmony_ci		return -ENOMEM;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	spin_lock(&dst_server->nfs_client->cl_lock);
19462306a36Sopenharmony_ci	list_for_each_entry(iter,
19562306a36Sopenharmony_ci				&dst_server->nfs_client->pending_cb_stateids,
19662306a36Sopenharmony_ci				copies) {
19762306a36Sopenharmony_ci		if (memcmp(&res->write_res.stateid, &iter->stateid,
19862306a36Sopenharmony_ci				NFS4_STATEID_SIZE))
19962306a36Sopenharmony_ci			continue;
20062306a36Sopenharmony_ci		tmp_copy = iter;
20162306a36Sopenharmony_ci		list_del(&iter->copies);
20262306a36Sopenharmony_ci		break;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci	if (tmp_copy) {
20562306a36Sopenharmony_ci		spin_unlock(&dst_server->nfs_client->cl_lock);
20662306a36Sopenharmony_ci		kfree(copy);
20762306a36Sopenharmony_ci		copy = tmp_copy;
20862306a36Sopenharmony_ci		goto out;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
21262306a36Sopenharmony_ci	init_completion(&copy->completion);
21362306a36Sopenharmony_ci	copy->parent_dst_state = dst_ctx->state;
21462306a36Sopenharmony_ci	copy->parent_src_state = src_ctx->state;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	list_add_tail(&copy->copies, &dst_server->ss_copies);
21762306a36Sopenharmony_ci	spin_unlock(&dst_server->nfs_client->cl_lock);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (dst_server != src_server) {
22062306a36Sopenharmony_ci		spin_lock(&src_server->nfs_client->cl_lock);
22162306a36Sopenharmony_ci		list_add_tail(&copy->src_copies, &src_server->ss_copies);
22262306a36Sopenharmony_ci		spin_unlock(&src_server->nfs_client->cl_lock);
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	status = wait_for_completion_interruptible(&copy->completion);
22662306a36Sopenharmony_ci	spin_lock(&dst_server->nfs_client->cl_lock);
22762306a36Sopenharmony_ci	list_del_init(&copy->copies);
22862306a36Sopenharmony_ci	spin_unlock(&dst_server->nfs_client->cl_lock);
22962306a36Sopenharmony_ci	if (dst_server != src_server) {
23062306a36Sopenharmony_ci		spin_lock(&src_server->nfs_client->cl_lock);
23162306a36Sopenharmony_ci		list_del_init(&copy->src_copies);
23262306a36Sopenharmony_ci		spin_unlock(&src_server->nfs_client->cl_lock);
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci	if (status == -ERESTARTSYS) {
23562306a36Sopenharmony_ci		goto out_cancel;
23662306a36Sopenharmony_ci	} else if (copy->flags || copy->error == NFS4ERR_PARTNER_NO_AUTH) {
23762306a36Sopenharmony_ci		status = -EAGAIN;
23862306a36Sopenharmony_ci		*restart = true;
23962306a36Sopenharmony_ci		goto out_cancel;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ciout:
24262306a36Sopenharmony_ci	res->write_res.count = copy->count;
24362306a36Sopenharmony_ci	memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
24462306a36Sopenharmony_ci	status = -copy->error;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ciout_free:
24762306a36Sopenharmony_ci	kfree(copy);
24862306a36Sopenharmony_ci	return status;
24962306a36Sopenharmony_ciout_cancel:
25062306a36Sopenharmony_ci	nfs42_do_offload_cancel_async(dst, &copy->stateid);
25162306a36Sopenharmony_ci	if (!nfs42_files_from_same_server(src, dst))
25262306a36Sopenharmony_ci		nfs42_do_offload_cancel_async(src, src_stateid);
25362306a36Sopenharmony_ci	goto out_free;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int process_copy_commit(struct file *dst, loff_t pos_dst,
25762306a36Sopenharmony_ci			       struct nfs42_copy_res *res)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct nfs_commitres cres;
26062306a36Sopenharmony_ci	int status = -ENOMEM;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	cres.verf = kzalloc(sizeof(struct nfs_writeverf), GFP_KERNEL);
26362306a36Sopenharmony_ci	if (!cres.verf)
26462306a36Sopenharmony_ci		goto out;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	status = nfs4_proc_commit(dst, pos_dst, res->write_res.count, &cres);
26762306a36Sopenharmony_ci	if (status)
26862306a36Sopenharmony_ci		goto out_free;
26962306a36Sopenharmony_ci	if (nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
27062306a36Sopenharmony_ci				    &cres.verf->verifier)) {
27162306a36Sopenharmony_ci		dprintk("commit verf differs from copy verf\n");
27262306a36Sopenharmony_ci		status = -EAGAIN;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ciout_free:
27562306a36Sopenharmony_ci	kfree(cres.verf);
27662306a36Sopenharmony_ciout:
27762306a36Sopenharmony_ci	return status;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/**
28162306a36Sopenharmony_ci * nfs42_copy_dest_done - perform inode cache updates after clone/copy offload
28262306a36Sopenharmony_ci * @inode: pointer to destination inode
28362306a36Sopenharmony_ci * @pos: destination offset
28462306a36Sopenharmony_ci * @len: copy length
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * Punch a hole in the inode page cache, so that the NFS client will
28762306a36Sopenharmony_ci * know to retrieve new data.
28862306a36Sopenharmony_ci * Update the file size if necessary, and then mark the inode as having
28962306a36Sopenharmony_ci * invalid cached values for change attribute, ctime, mtime and space used.
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_cistatic void nfs42_copy_dest_done(struct inode *inode, loff_t pos, loff_t len)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	loff_t newsize = pos + len;
29462306a36Sopenharmony_ci	loff_t end = newsize - 1;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	WARN_ON_ONCE(invalidate_inode_pages2_range(inode->i_mapping,
29762306a36Sopenharmony_ci				pos >> PAGE_SHIFT, end >> PAGE_SHIFT));
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
30062306a36Sopenharmony_ci	if (newsize > i_size_read(inode))
30162306a36Sopenharmony_ci		i_size_write(inode, newsize);
30262306a36Sopenharmony_ci	nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE |
30362306a36Sopenharmony_ci					     NFS_INO_INVALID_CTIME |
30462306a36Sopenharmony_ci					     NFS_INO_INVALID_MTIME |
30562306a36Sopenharmony_ci					     NFS_INO_INVALID_BLOCKS);
30662306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic ssize_t _nfs42_proc_copy(struct file *src,
31062306a36Sopenharmony_ci				struct nfs_lock_context *src_lock,
31162306a36Sopenharmony_ci				struct file *dst,
31262306a36Sopenharmony_ci				struct nfs_lock_context *dst_lock,
31362306a36Sopenharmony_ci				struct nfs42_copy_args *args,
31462306a36Sopenharmony_ci				struct nfs42_copy_res *res,
31562306a36Sopenharmony_ci				struct nl4_server *nss,
31662306a36Sopenharmony_ci				nfs4_stateid *cnr_stateid,
31762306a36Sopenharmony_ci				bool *restart)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct rpc_message msg = {
32062306a36Sopenharmony_ci		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY],
32162306a36Sopenharmony_ci		.rpc_argp = args,
32262306a36Sopenharmony_ci		.rpc_resp = res,
32362306a36Sopenharmony_ci	};
32462306a36Sopenharmony_ci	struct inode *dst_inode = file_inode(dst);
32562306a36Sopenharmony_ci	struct inode *src_inode = file_inode(src);
32662306a36Sopenharmony_ci	struct nfs_server *dst_server = NFS_SERVER(dst_inode);
32762306a36Sopenharmony_ci	struct nfs_server *src_server = NFS_SERVER(src_inode);
32862306a36Sopenharmony_ci	loff_t pos_src = args->src_pos;
32962306a36Sopenharmony_ci	loff_t pos_dst = args->dst_pos;
33062306a36Sopenharmony_ci	size_t count = args->count;
33162306a36Sopenharmony_ci	ssize_t status;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (nss) {
33462306a36Sopenharmony_ci		args->cp_src = nss;
33562306a36Sopenharmony_ci		nfs4_stateid_copy(&args->src_stateid, cnr_stateid);
33662306a36Sopenharmony_ci	} else {
33762306a36Sopenharmony_ci		status = nfs4_set_rw_stateid(&args->src_stateid,
33862306a36Sopenharmony_ci				src_lock->open_context, src_lock, FMODE_READ);
33962306a36Sopenharmony_ci		if (status) {
34062306a36Sopenharmony_ci			if (status == -EAGAIN)
34162306a36Sopenharmony_ci				status = -NFS4ERR_BAD_STATEID;
34262306a36Sopenharmony_ci			return status;
34362306a36Sopenharmony_ci		}
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci	status = nfs_filemap_write_and_wait_range(src->f_mapping,
34662306a36Sopenharmony_ci			pos_src, pos_src + (loff_t)count - 1);
34762306a36Sopenharmony_ci	if (status)
34862306a36Sopenharmony_ci		return status;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	status = nfs4_set_rw_stateid(&args->dst_stateid, dst_lock->open_context,
35162306a36Sopenharmony_ci				     dst_lock, FMODE_WRITE);
35262306a36Sopenharmony_ci	if (status) {
35362306a36Sopenharmony_ci		if (status == -EAGAIN)
35462306a36Sopenharmony_ci			status = -NFS4ERR_BAD_STATEID;
35562306a36Sopenharmony_ci		return status;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	status = nfs_sync_inode(dst_inode);
35962306a36Sopenharmony_ci	if (status)
36062306a36Sopenharmony_ci		return status;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	res->commit_res.verf = NULL;
36362306a36Sopenharmony_ci	if (args->sync) {
36462306a36Sopenharmony_ci		res->commit_res.verf =
36562306a36Sopenharmony_ci			kzalloc(sizeof(struct nfs_writeverf), GFP_KERNEL);
36662306a36Sopenharmony_ci		if (!res->commit_res.verf)
36762306a36Sopenharmony_ci			return -ENOMEM;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci	set_bit(NFS_CLNT_SRC_SSC_COPY_STATE,
37062306a36Sopenharmony_ci		&src_lock->open_context->state->flags);
37162306a36Sopenharmony_ci	set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
37262306a36Sopenharmony_ci		&dst_lock->open_context->state->flags);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	status = nfs4_call_sync(dst_server->client, dst_server, &msg,
37562306a36Sopenharmony_ci				&args->seq_args, &res->seq_res, 0);
37662306a36Sopenharmony_ci	trace_nfs4_copy(src_inode, dst_inode, args, res, nss, status);
37762306a36Sopenharmony_ci	if (status == -ENOTSUPP)
37862306a36Sopenharmony_ci		dst_server->caps &= ~NFS_CAP_COPY;
37962306a36Sopenharmony_ci	if (status)
38062306a36Sopenharmony_ci		goto out;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (args->sync &&
38362306a36Sopenharmony_ci		nfs_write_verifier_cmp(&res->write_res.verifier.verifier,
38462306a36Sopenharmony_ci				    &res->commit_res.verf->verifier)) {
38562306a36Sopenharmony_ci		status = -EAGAIN;
38662306a36Sopenharmony_ci		goto out;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (!res->synchronous) {
39062306a36Sopenharmony_ci		status = handle_async_copy(res, dst_server, src_server, src,
39162306a36Sopenharmony_ci				dst, &args->src_stateid, restart);
39262306a36Sopenharmony_ci		if (status)
39362306a36Sopenharmony_ci			goto out;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if ((!res->synchronous || !args->sync) &&
39762306a36Sopenharmony_ci			res->write_res.verifier.committed != NFS_FILE_SYNC) {
39862306a36Sopenharmony_ci		status = process_copy_commit(dst, pos_dst, res);
39962306a36Sopenharmony_ci		if (status)
40062306a36Sopenharmony_ci			goto out;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	nfs42_copy_dest_done(dst_inode, pos_dst, res->write_res.count);
40462306a36Sopenharmony_ci	nfs_invalidate_atime(src_inode);
40562306a36Sopenharmony_ci	status = res->write_res.count;
40662306a36Sopenharmony_ciout:
40762306a36Sopenharmony_ci	if (args->sync)
40862306a36Sopenharmony_ci		kfree(res->commit_res.verf);
40962306a36Sopenharmony_ci	return status;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cissize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
41362306a36Sopenharmony_ci			struct file *dst, loff_t pos_dst, size_t count,
41462306a36Sopenharmony_ci			struct nl4_server *nss,
41562306a36Sopenharmony_ci			nfs4_stateid *cnr_stateid, bool sync)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(file_inode(dst));
41862306a36Sopenharmony_ci	struct nfs_lock_context *src_lock;
41962306a36Sopenharmony_ci	struct nfs_lock_context *dst_lock;
42062306a36Sopenharmony_ci	struct nfs42_copy_args args = {
42162306a36Sopenharmony_ci		.src_fh		= NFS_FH(file_inode(src)),
42262306a36Sopenharmony_ci		.src_pos	= pos_src,
42362306a36Sopenharmony_ci		.dst_fh		= NFS_FH(file_inode(dst)),
42462306a36Sopenharmony_ci		.dst_pos	= pos_dst,
42562306a36Sopenharmony_ci		.count		= count,
42662306a36Sopenharmony_ci		.sync		= sync,
42762306a36Sopenharmony_ci	};
42862306a36Sopenharmony_ci	struct nfs42_copy_res res;
42962306a36Sopenharmony_ci	struct nfs4_exception src_exception = {
43062306a36Sopenharmony_ci		.inode		= file_inode(src),
43162306a36Sopenharmony_ci		.stateid	= &args.src_stateid,
43262306a36Sopenharmony_ci	};
43362306a36Sopenharmony_ci	struct nfs4_exception dst_exception = {
43462306a36Sopenharmony_ci		.inode		= file_inode(dst),
43562306a36Sopenharmony_ci		.stateid	= &args.dst_stateid,
43662306a36Sopenharmony_ci	};
43762306a36Sopenharmony_ci	ssize_t err, err2;
43862306a36Sopenharmony_ci	bool restart = false;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	src_lock = nfs_get_lock_context(nfs_file_open_context(src));
44162306a36Sopenharmony_ci	if (IS_ERR(src_lock))
44262306a36Sopenharmony_ci		return PTR_ERR(src_lock);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	src_exception.state = src_lock->open_context->state;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst));
44762306a36Sopenharmony_ci	if (IS_ERR(dst_lock)) {
44862306a36Sopenharmony_ci		err = PTR_ERR(dst_lock);
44962306a36Sopenharmony_ci		goto out_put_src_lock;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	dst_exception.state = dst_lock->open_context->state;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	do {
45562306a36Sopenharmony_ci		inode_lock(file_inode(dst));
45662306a36Sopenharmony_ci		err = _nfs42_proc_copy(src, src_lock,
45762306a36Sopenharmony_ci				dst, dst_lock,
45862306a36Sopenharmony_ci				&args, &res,
45962306a36Sopenharmony_ci				nss, cnr_stateid, &restart);
46062306a36Sopenharmony_ci		inode_unlock(file_inode(dst));
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		if (err >= 0)
46362306a36Sopenharmony_ci			break;
46462306a36Sopenharmony_ci		if ((err == -ENOTSUPP ||
46562306a36Sopenharmony_ci				err == -NFS4ERR_OFFLOAD_DENIED) &&
46662306a36Sopenharmony_ci				nfs42_files_from_same_server(src, dst)) {
46762306a36Sopenharmony_ci			err = -EOPNOTSUPP;
46862306a36Sopenharmony_ci			break;
46962306a36Sopenharmony_ci		} else if (err == -EAGAIN) {
47062306a36Sopenharmony_ci			if (!restart) {
47162306a36Sopenharmony_ci				dst_exception.retry = 1;
47262306a36Sopenharmony_ci				continue;
47362306a36Sopenharmony_ci			}
47462306a36Sopenharmony_ci			break;
47562306a36Sopenharmony_ci		} else if (err == -NFS4ERR_OFFLOAD_NO_REQS &&
47662306a36Sopenharmony_ci				args.sync != res.synchronous) {
47762306a36Sopenharmony_ci			args.sync = res.synchronous;
47862306a36Sopenharmony_ci			dst_exception.retry = 1;
47962306a36Sopenharmony_ci			continue;
48062306a36Sopenharmony_ci		} else if ((err == -ESTALE ||
48162306a36Sopenharmony_ci				err == -NFS4ERR_OFFLOAD_DENIED ||
48262306a36Sopenharmony_ci				err == -ENOTSUPP) &&
48362306a36Sopenharmony_ci				!nfs42_files_from_same_server(src, dst)) {
48462306a36Sopenharmony_ci			nfs42_do_offload_cancel_async(src, &args.src_stateid);
48562306a36Sopenharmony_ci			err = -EOPNOTSUPP;
48662306a36Sopenharmony_ci			break;
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		err2 = nfs4_handle_exception(server, err, &src_exception);
49062306a36Sopenharmony_ci		err  = nfs4_handle_exception(server, err, &dst_exception);
49162306a36Sopenharmony_ci		if (!err)
49262306a36Sopenharmony_ci			err = err2;
49362306a36Sopenharmony_ci	} while (src_exception.retry || dst_exception.retry);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	nfs_put_lock_context(dst_lock);
49662306a36Sopenharmony_ciout_put_src_lock:
49762306a36Sopenharmony_ci	nfs_put_lock_context(src_lock);
49862306a36Sopenharmony_ci	return err;
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistruct nfs42_offloadcancel_data {
50262306a36Sopenharmony_ci	struct nfs_server *seq_server;
50362306a36Sopenharmony_ci	struct nfs42_offload_status_args args;
50462306a36Sopenharmony_ci	struct nfs42_offload_status_res res;
50562306a36Sopenharmony_ci};
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic void nfs42_offload_cancel_prepare(struct rpc_task *task, void *calldata)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct nfs42_offloadcancel_data *data = calldata;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	nfs4_setup_sequence(data->seq_server->nfs_client,
51262306a36Sopenharmony_ci				&data->args.osa_seq_args,
51362306a36Sopenharmony_ci				&data->res.osr_seq_res, task);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic void nfs42_offload_cancel_done(struct rpc_task *task, void *calldata)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct nfs42_offloadcancel_data *data = calldata;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	trace_nfs4_offload_cancel(&data->args, task->tk_status);
52162306a36Sopenharmony_ci	nfs41_sequence_done(task, &data->res.osr_seq_res);
52262306a36Sopenharmony_ci	if (task->tk_status &&
52362306a36Sopenharmony_ci		nfs4_async_handle_error(task, data->seq_server, NULL,
52462306a36Sopenharmony_ci			NULL) == -EAGAIN)
52562306a36Sopenharmony_ci		rpc_restart_call_prepare(task);
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic void nfs42_free_offloadcancel_data(void *data)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	kfree(data);
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic const struct rpc_call_ops nfs42_offload_cancel_ops = {
53462306a36Sopenharmony_ci	.rpc_call_prepare = nfs42_offload_cancel_prepare,
53562306a36Sopenharmony_ci	.rpc_call_done = nfs42_offload_cancel_done,
53662306a36Sopenharmony_ci	.rpc_release = nfs42_free_offloadcancel_data,
53762306a36Sopenharmony_ci};
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int nfs42_do_offload_cancel_async(struct file *dst,
54062306a36Sopenharmony_ci					 nfs4_stateid *stateid)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct nfs_server *dst_server = NFS_SERVER(file_inode(dst));
54362306a36Sopenharmony_ci	struct nfs42_offloadcancel_data *data = NULL;
54462306a36Sopenharmony_ci	struct nfs_open_context *ctx = nfs_file_open_context(dst);
54562306a36Sopenharmony_ci	struct rpc_task *task;
54662306a36Sopenharmony_ci	struct rpc_message msg = {
54762306a36Sopenharmony_ci		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OFFLOAD_CANCEL],
54862306a36Sopenharmony_ci		.rpc_cred = ctx->cred,
54962306a36Sopenharmony_ci	};
55062306a36Sopenharmony_ci	struct rpc_task_setup task_setup_data = {
55162306a36Sopenharmony_ci		.rpc_client = dst_server->client,
55262306a36Sopenharmony_ci		.rpc_message = &msg,
55362306a36Sopenharmony_ci		.callback_ops = &nfs42_offload_cancel_ops,
55462306a36Sopenharmony_ci		.workqueue = nfsiod_workqueue,
55562306a36Sopenharmony_ci		.flags = RPC_TASK_ASYNC,
55662306a36Sopenharmony_ci	};
55762306a36Sopenharmony_ci	int status;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (!(dst_server->caps & NFS_CAP_OFFLOAD_CANCEL))
56062306a36Sopenharmony_ci		return -EOPNOTSUPP;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	data = kzalloc(sizeof(struct nfs42_offloadcancel_data), GFP_KERNEL);
56362306a36Sopenharmony_ci	if (data == NULL)
56462306a36Sopenharmony_ci		return -ENOMEM;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	data->seq_server = dst_server;
56762306a36Sopenharmony_ci	data->args.osa_src_fh = NFS_FH(file_inode(dst));
56862306a36Sopenharmony_ci	memcpy(&data->args.osa_stateid, stateid,
56962306a36Sopenharmony_ci		sizeof(data->args.osa_stateid));
57062306a36Sopenharmony_ci	msg.rpc_argp = &data->args;
57162306a36Sopenharmony_ci	msg.rpc_resp = &data->res;
57262306a36Sopenharmony_ci	task_setup_data.callback_data = data;
57362306a36Sopenharmony_ci	nfs4_init_sequence(&data->args.osa_seq_args, &data->res.osr_seq_res,
57462306a36Sopenharmony_ci			   1, 0);
57562306a36Sopenharmony_ci	task = rpc_run_task(&task_setup_data);
57662306a36Sopenharmony_ci	if (IS_ERR(task))
57762306a36Sopenharmony_ci		return PTR_ERR(task);
57862306a36Sopenharmony_ci	status = rpc_wait_for_completion_task(task);
57962306a36Sopenharmony_ci	if (status == -ENOTSUPP)
58062306a36Sopenharmony_ci		dst_server->caps &= ~NFS_CAP_OFFLOAD_CANCEL;
58162306a36Sopenharmony_ci	rpc_put_task(task);
58262306a36Sopenharmony_ci	return status;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int _nfs42_proc_copy_notify(struct file *src, struct file *dst,
58662306a36Sopenharmony_ci				   struct nfs42_copy_notify_args *args,
58762306a36Sopenharmony_ci				   struct nfs42_copy_notify_res *res)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
59062306a36Sopenharmony_ci	struct rpc_message msg = {
59162306a36Sopenharmony_ci		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY_NOTIFY],
59262306a36Sopenharmony_ci		.rpc_argp = args,
59362306a36Sopenharmony_ci		.rpc_resp = res,
59462306a36Sopenharmony_ci	};
59562306a36Sopenharmony_ci	int status;
59662306a36Sopenharmony_ci	struct nfs_open_context *ctx;
59762306a36Sopenharmony_ci	struct nfs_lock_context *l_ctx;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	ctx = get_nfs_open_context(nfs_file_open_context(src));
60062306a36Sopenharmony_ci	l_ctx = nfs_get_lock_context(ctx);
60162306a36Sopenharmony_ci	if (IS_ERR(l_ctx)) {
60262306a36Sopenharmony_ci		status = PTR_ERR(l_ctx);
60362306a36Sopenharmony_ci		goto out;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	status = nfs4_set_rw_stateid(&args->cna_src_stateid, ctx, l_ctx,
60762306a36Sopenharmony_ci				     FMODE_READ);
60862306a36Sopenharmony_ci	nfs_put_lock_context(l_ctx);
60962306a36Sopenharmony_ci	if (status) {
61062306a36Sopenharmony_ci		if (status == -EAGAIN)
61162306a36Sopenharmony_ci			status = -NFS4ERR_BAD_STATEID;
61262306a36Sopenharmony_ci		goto out;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	status = nfs4_call_sync(src_server->client, src_server, &msg,
61662306a36Sopenharmony_ci				&args->cna_seq_args, &res->cnr_seq_res, 0);
61762306a36Sopenharmony_ci	trace_nfs4_copy_notify(file_inode(src), args, res, status);
61862306a36Sopenharmony_ci	if (status == -ENOTSUPP)
61962306a36Sopenharmony_ci		src_server->caps &= ~NFS_CAP_COPY_NOTIFY;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ciout:
62262306a36Sopenharmony_ci	put_nfs_open_context(nfs_file_open_context(src));
62362306a36Sopenharmony_ci	return status;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ciint nfs42_proc_copy_notify(struct file *src, struct file *dst,
62762306a36Sopenharmony_ci				struct nfs42_copy_notify_res *res)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct nfs_server *src_server = NFS_SERVER(file_inode(src));
63062306a36Sopenharmony_ci	struct nfs42_copy_notify_args *args;
63162306a36Sopenharmony_ci	struct nfs4_exception exception = {
63262306a36Sopenharmony_ci		.inode = file_inode(src),
63362306a36Sopenharmony_ci	};
63462306a36Sopenharmony_ci	int status;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (!(src_server->caps & NFS_CAP_COPY_NOTIFY))
63762306a36Sopenharmony_ci		return -EOPNOTSUPP;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	args = kzalloc(sizeof(struct nfs42_copy_notify_args), GFP_KERNEL);
64062306a36Sopenharmony_ci	if (args == NULL)
64162306a36Sopenharmony_ci		return -ENOMEM;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	args->cna_src_fh  = NFS_FH(file_inode(src)),
64462306a36Sopenharmony_ci	args->cna_dst.nl4_type = NL4_NETADDR;
64562306a36Sopenharmony_ci	nfs42_set_netaddr(dst, &args->cna_dst.u.nl4_addr);
64662306a36Sopenharmony_ci	exception.stateid = &args->cna_src_stateid;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	do {
64962306a36Sopenharmony_ci		status = _nfs42_proc_copy_notify(src, dst, args, res);
65062306a36Sopenharmony_ci		if (status == -ENOTSUPP) {
65162306a36Sopenharmony_ci			status = -EOPNOTSUPP;
65262306a36Sopenharmony_ci			goto out;
65362306a36Sopenharmony_ci		}
65462306a36Sopenharmony_ci		status = nfs4_handle_exception(src_server, status, &exception);
65562306a36Sopenharmony_ci	} while (exception.retry);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ciout:
65862306a36Sopenharmony_ci	kfree(args);
65962306a36Sopenharmony_ci	return status;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic loff_t _nfs42_proc_llseek(struct file *filep,
66362306a36Sopenharmony_ci		struct nfs_lock_context *lock, loff_t offset, int whence)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	struct inode *inode = file_inode(filep);
66662306a36Sopenharmony_ci	struct nfs42_seek_args args = {
66762306a36Sopenharmony_ci		.sa_fh		= NFS_FH(inode),
66862306a36Sopenharmony_ci		.sa_offset	= offset,
66962306a36Sopenharmony_ci		.sa_what	= (whence == SEEK_HOLE) ?
67062306a36Sopenharmony_ci					NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA,
67162306a36Sopenharmony_ci	};
67262306a36Sopenharmony_ci	struct nfs42_seek_res res;
67362306a36Sopenharmony_ci	struct rpc_message msg = {
67462306a36Sopenharmony_ci		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK],
67562306a36Sopenharmony_ci		.rpc_argp = &args,
67662306a36Sopenharmony_ci		.rpc_resp = &res,
67762306a36Sopenharmony_ci	};
67862306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
67962306a36Sopenharmony_ci	int status;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (!nfs_server_capable(inode, NFS_CAP_SEEK))
68262306a36Sopenharmony_ci		return -ENOTSUPP;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context,
68562306a36Sopenharmony_ci			lock, FMODE_READ);
68662306a36Sopenharmony_ci	if (status) {
68762306a36Sopenharmony_ci		if (status == -EAGAIN)
68862306a36Sopenharmony_ci			status = -NFS4ERR_BAD_STATEID;
68962306a36Sopenharmony_ci		return status;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	status = nfs_filemap_write_and_wait_range(inode->i_mapping,
69362306a36Sopenharmony_ci			offset, LLONG_MAX);
69462306a36Sopenharmony_ci	if (status)
69562306a36Sopenharmony_ci		return status;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	status = nfs4_call_sync(server->client, server, &msg,
69862306a36Sopenharmony_ci				&args.seq_args, &res.seq_res, 0);
69962306a36Sopenharmony_ci	trace_nfs4_llseek(inode, &args, &res, status);
70062306a36Sopenharmony_ci	if (status == -ENOTSUPP)
70162306a36Sopenharmony_ci		server->caps &= ~NFS_CAP_SEEK;
70262306a36Sopenharmony_ci	if (status)
70362306a36Sopenharmony_ci		return status;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (whence == SEEK_DATA && res.sr_eof)
70662306a36Sopenharmony_ci		return -NFS4ERR_NXIO;
70762306a36Sopenharmony_ci	else
70862306a36Sopenharmony_ci		return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes);
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ciloff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(file_inode(filep));
71462306a36Sopenharmony_ci	struct nfs4_exception exception = { };
71562306a36Sopenharmony_ci	struct nfs_lock_context *lock;
71662306a36Sopenharmony_ci	loff_t err;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	lock = nfs_get_lock_context(nfs_file_open_context(filep));
71962306a36Sopenharmony_ci	if (IS_ERR(lock))
72062306a36Sopenharmony_ci		return PTR_ERR(lock);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	exception.inode = file_inode(filep);
72362306a36Sopenharmony_ci	exception.state = lock->open_context->state;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	do {
72662306a36Sopenharmony_ci		err = _nfs42_proc_llseek(filep, lock, offset, whence);
72762306a36Sopenharmony_ci		if (err >= 0)
72862306a36Sopenharmony_ci			break;
72962306a36Sopenharmony_ci		if (err == -ENOTSUPP) {
73062306a36Sopenharmony_ci			err = -EOPNOTSUPP;
73162306a36Sopenharmony_ci			break;
73262306a36Sopenharmony_ci		}
73362306a36Sopenharmony_ci		err = nfs4_handle_exception(server, err, &exception);
73462306a36Sopenharmony_ci	} while (exception.retry);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	nfs_put_lock_context(lock);
73762306a36Sopenharmony_ci	return err;
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic void
74262306a36Sopenharmony_cinfs42_layoutstat_prepare(struct rpc_task *task, void *calldata)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct nfs42_layoutstat_data *data = calldata;
74562306a36Sopenharmony_ci	struct inode *inode = data->inode;
74662306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
74762306a36Sopenharmony_ci	struct pnfs_layout_hdr *lo;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
75062306a36Sopenharmony_ci	lo = NFS_I(inode)->layout;
75162306a36Sopenharmony_ci	if (!pnfs_layout_is_valid(lo)) {
75262306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
75362306a36Sopenharmony_ci		rpc_exit(task, 0);
75462306a36Sopenharmony_ci		return;
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci	nfs4_stateid_copy(&data->args.stateid, &lo->plh_stateid);
75762306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
75862306a36Sopenharmony_ci	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
75962306a36Sopenharmony_ci			    &data->res.seq_res, task);
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic void
76362306a36Sopenharmony_cinfs42_layoutstat_done(struct rpc_task *task, void *calldata)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct nfs42_layoutstat_data *data = calldata;
76662306a36Sopenharmony_ci	struct inode *inode = data->inode;
76762306a36Sopenharmony_ci	struct pnfs_layout_hdr *lo;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	if (!nfs4_sequence_done(task, &data->res.seq_res))
77062306a36Sopenharmony_ci		return;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	switch (task->tk_status) {
77362306a36Sopenharmony_ci	case 0:
77462306a36Sopenharmony_ci		return;
77562306a36Sopenharmony_ci	case -NFS4ERR_BADHANDLE:
77662306a36Sopenharmony_ci	case -ESTALE:
77762306a36Sopenharmony_ci		pnfs_destroy_layout(NFS_I(inode));
77862306a36Sopenharmony_ci		break;
77962306a36Sopenharmony_ci	case -NFS4ERR_EXPIRED:
78062306a36Sopenharmony_ci	case -NFS4ERR_ADMIN_REVOKED:
78162306a36Sopenharmony_ci	case -NFS4ERR_DELEG_REVOKED:
78262306a36Sopenharmony_ci	case -NFS4ERR_STALE_STATEID:
78362306a36Sopenharmony_ci	case -NFS4ERR_BAD_STATEID:
78462306a36Sopenharmony_ci		spin_lock(&inode->i_lock);
78562306a36Sopenharmony_ci		lo = NFS_I(inode)->layout;
78662306a36Sopenharmony_ci		if (pnfs_layout_is_valid(lo) &&
78762306a36Sopenharmony_ci		    nfs4_stateid_match(&data->args.stateid,
78862306a36Sopenharmony_ci					     &lo->plh_stateid)) {
78962306a36Sopenharmony_ci			LIST_HEAD(head);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci			/*
79262306a36Sopenharmony_ci			 * Mark the bad layout state as invalid, then retry
79362306a36Sopenharmony_ci			 * with the current stateid.
79462306a36Sopenharmony_ci			 */
79562306a36Sopenharmony_ci			pnfs_mark_layout_stateid_invalid(lo, &head);
79662306a36Sopenharmony_ci			spin_unlock(&inode->i_lock);
79762306a36Sopenharmony_ci			pnfs_free_lseg_list(&head);
79862306a36Sopenharmony_ci			nfs_commit_inode(inode, 0);
79962306a36Sopenharmony_ci		} else
80062306a36Sopenharmony_ci			spin_unlock(&inode->i_lock);
80162306a36Sopenharmony_ci		break;
80262306a36Sopenharmony_ci	case -NFS4ERR_OLD_STATEID:
80362306a36Sopenharmony_ci		spin_lock(&inode->i_lock);
80462306a36Sopenharmony_ci		lo = NFS_I(inode)->layout;
80562306a36Sopenharmony_ci		if (pnfs_layout_is_valid(lo) &&
80662306a36Sopenharmony_ci		    nfs4_stateid_match_other(&data->args.stateid,
80762306a36Sopenharmony_ci					&lo->plh_stateid)) {
80862306a36Sopenharmony_ci			/* Do we need to delay before resending? */
80962306a36Sopenharmony_ci			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
81062306a36Sopenharmony_ci						&data->args.stateid))
81162306a36Sopenharmony_ci				rpc_delay(task, HZ);
81262306a36Sopenharmony_ci			rpc_restart_call_prepare(task);
81362306a36Sopenharmony_ci		}
81462306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
81562306a36Sopenharmony_ci		break;
81662306a36Sopenharmony_ci	case -ENOTSUPP:
81762306a36Sopenharmony_ci	case -EOPNOTSUPP:
81862306a36Sopenharmony_ci		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	trace_nfs4_layoutstats(inode, &data->args.stateid, task->tk_status);
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic void
82562306a36Sopenharmony_cinfs42_layoutstat_release(void *calldata)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct nfs42_layoutstat_data *data = calldata;
82862306a36Sopenharmony_ci	struct nfs42_layoutstat_devinfo *devinfo = data->args.devinfo;
82962306a36Sopenharmony_ci	int i;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	for (i = 0; i < data->args.num_dev; i++) {
83262306a36Sopenharmony_ci		if (devinfo[i].ld_private.ops && devinfo[i].ld_private.ops->free)
83362306a36Sopenharmony_ci			devinfo[i].ld_private.ops->free(&devinfo[i].ld_private);
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout);
83762306a36Sopenharmony_ci	smp_mb__before_atomic();
83862306a36Sopenharmony_ci	clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags);
83962306a36Sopenharmony_ci	smp_mb__after_atomic();
84062306a36Sopenharmony_ci	nfs_iput_and_deactive(data->inode);
84162306a36Sopenharmony_ci	kfree(data->args.devinfo);
84262306a36Sopenharmony_ci	kfree(data);
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cistatic const struct rpc_call_ops nfs42_layoutstat_ops = {
84662306a36Sopenharmony_ci	.rpc_call_prepare = nfs42_layoutstat_prepare,
84762306a36Sopenharmony_ci	.rpc_call_done = nfs42_layoutstat_done,
84862306a36Sopenharmony_ci	.rpc_release = nfs42_layoutstat_release,
84962306a36Sopenharmony_ci};
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ciint nfs42_proc_layoutstats_generic(struct nfs_server *server,
85262306a36Sopenharmony_ci				   struct nfs42_layoutstat_data *data)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	struct rpc_message msg = {
85562306a36Sopenharmony_ci		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS],
85662306a36Sopenharmony_ci		.rpc_argp = &data->args,
85762306a36Sopenharmony_ci		.rpc_resp = &data->res,
85862306a36Sopenharmony_ci	};
85962306a36Sopenharmony_ci	struct rpc_task_setup task_setup = {
86062306a36Sopenharmony_ci		.rpc_client = server->client,
86162306a36Sopenharmony_ci		.rpc_message = &msg,
86262306a36Sopenharmony_ci		.callback_ops = &nfs42_layoutstat_ops,
86362306a36Sopenharmony_ci		.callback_data = data,
86462306a36Sopenharmony_ci		.flags = RPC_TASK_ASYNC,
86562306a36Sopenharmony_ci	};
86662306a36Sopenharmony_ci	struct rpc_task *task;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	data->inode = nfs_igrab_and_active(data->args.inode);
86962306a36Sopenharmony_ci	if (!data->inode) {
87062306a36Sopenharmony_ci		nfs42_layoutstat_release(data);
87162306a36Sopenharmony_ci		return -EAGAIN;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
87462306a36Sopenharmony_ci	task = rpc_run_task(&task_setup);
87562306a36Sopenharmony_ci	if (IS_ERR(task))
87662306a36Sopenharmony_ci		return PTR_ERR(task);
87762306a36Sopenharmony_ci	rpc_put_task(task);
87862306a36Sopenharmony_ci	return 0;
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic struct nfs42_layouterror_data *
88262306a36Sopenharmony_cinfs42_alloc_layouterror_data(struct pnfs_layout_segment *lseg, gfp_t gfp_flags)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct nfs42_layouterror_data *data;
88562306a36Sopenharmony_ci	struct inode *inode = lseg->pls_layout->plh_inode;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	data = kzalloc(sizeof(*data), gfp_flags);
88862306a36Sopenharmony_ci	if (data) {
88962306a36Sopenharmony_ci		data->args.inode = data->inode = nfs_igrab_and_active(inode);
89062306a36Sopenharmony_ci		if (data->inode) {
89162306a36Sopenharmony_ci			data->lseg = pnfs_get_lseg(lseg);
89262306a36Sopenharmony_ci			if (data->lseg)
89362306a36Sopenharmony_ci				return data;
89462306a36Sopenharmony_ci			nfs_iput_and_deactive(data->inode);
89562306a36Sopenharmony_ci		}
89662306a36Sopenharmony_ci		kfree(data);
89762306a36Sopenharmony_ci	}
89862306a36Sopenharmony_ci	return NULL;
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic void
90262306a36Sopenharmony_cinfs42_free_layouterror_data(struct nfs42_layouterror_data *data)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	pnfs_put_lseg(data->lseg);
90562306a36Sopenharmony_ci	nfs_iput_and_deactive(data->inode);
90662306a36Sopenharmony_ci	kfree(data);
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic void
91062306a36Sopenharmony_cinfs42_layouterror_prepare(struct rpc_task *task, void *calldata)
91162306a36Sopenharmony_ci{
91262306a36Sopenharmony_ci	struct nfs42_layouterror_data *data = calldata;
91362306a36Sopenharmony_ci	struct inode *inode = data->inode;
91462306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
91562306a36Sopenharmony_ci	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
91662306a36Sopenharmony_ci	unsigned i;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
91962306a36Sopenharmony_ci	if (!pnfs_layout_is_valid(lo)) {
92062306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
92162306a36Sopenharmony_ci		rpc_exit(task, 0);
92262306a36Sopenharmony_ci		return;
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci	for (i = 0; i < data->args.num_errors; i++)
92562306a36Sopenharmony_ci		nfs4_stateid_copy(&data->args.errors[i].stateid,
92662306a36Sopenharmony_ci				&lo->plh_stateid);
92762306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
92862306a36Sopenharmony_ci	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
92962306a36Sopenharmony_ci			    &data->res.seq_res, task);
93062306a36Sopenharmony_ci}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_cistatic void
93362306a36Sopenharmony_cinfs42_layouterror_done(struct rpc_task *task, void *calldata)
93462306a36Sopenharmony_ci{
93562306a36Sopenharmony_ci	struct nfs42_layouterror_data *data = calldata;
93662306a36Sopenharmony_ci	struct inode *inode = data->inode;
93762306a36Sopenharmony_ci	struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	if (!nfs4_sequence_done(task, &data->res.seq_res))
94062306a36Sopenharmony_ci		return;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	switch (task->tk_status) {
94362306a36Sopenharmony_ci	case 0:
94462306a36Sopenharmony_ci		return;
94562306a36Sopenharmony_ci	case -NFS4ERR_BADHANDLE:
94662306a36Sopenharmony_ci	case -ESTALE:
94762306a36Sopenharmony_ci		pnfs_destroy_layout(NFS_I(inode));
94862306a36Sopenharmony_ci		break;
94962306a36Sopenharmony_ci	case -NFS4ERR_EXPIRED:
95062306a36Sopenharmony_ci	case -NFS4ERR_ADMIN_REVOKED:
95162306a36Sopenharmony_ci	case -NFS4ERR_DELEG_REVOKED:
95262306a36Sopenharmony_ci	case -NFS4ERR_STALE_STATEID:
95362306a36Sopenharmony_ci	case -NFS4ERR_BAD_STATEID:
95462306a36Sopenharmony_ci		spin_lock(&inode->i_lock);
95562306a36Sopenharmony_ci		if (pnfs_layout_is_valid(lo) &&
95662306a36Sopenharmony_ci		    nfs4_stateid_match(&data->args.errors[0].stateid,
95762306a36Sopenharmony_ci					     &lo->plh_stateid)) {
95862306a36Sopenharmony_ci			LIST_HEAD(head);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci			/*
96162306a36Sopenharmony_ci			 * Mark the bad layout state as invalid, then retry
96262306a36Sopenharmony_ci			 * with the current stateid.
96362306a36Sopenharmony_ci			 */
96462306a36Sopenharmony_ci			pnfs_mark_layout_stateid_invalid(lo, &head);
96562306a36Sopenharmony_ci			spin_unlock(&inode->i_lock);
96662306a36Sopenharmony_ci			pnfs_free_lseg_list(&head);
96762306a36Sopenharmony_ci			nfs_commit_inode(inode, 0);
96862306a36Sopenharmony_ci		} else
96962306a36Sopenharmony_ci			spin_unlock(&inode->i_lock);
97062306a36Sopenharmony_ci		break;
97162306a36Sopenharmony_ci	case -NFS4ERR_OLD_STATEID:
97262306a36Sopenharmony_ci		spin_lock(&inode->i_lock);
97362306a36Sopenharmony_ci		if (pnfs_layout_is_valid(lo) &&
97462306a36Sopenharmony_ci		    nfs4_stateid_match_other(&data->args.errors[0].stateid,
97562306a36Sopenharmony_ci					&lo->plh_stateid)) {
97662306a36Sopenharmony_ci			/* Do we need to delay before resending? */
97762306a36Sopenharmony_ci			if (!nfs4_stateid_is_newer(&lo->plh_stateid,
97862306a36Sopenharmony_ci						&data->args.errors[0].stateid))
97962306a36Sopenharmony_ci				rpc_delay(task, HZ);
98062306a36Sopenharmony_ci			rpc_restart_call_prepare(task);
98162306a36Sopenharmony_ci		}
98262306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
98362306a36Sopenharmony_ci		break;
98462306a36Sopenharmony_ci	case -ENOTSUPP:
98562306a36Sopenharmony_ci	case -EOPNOTSUPP:
98662306a36Sopenharmony_ci		NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTERROR;
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	trace_nfs4_layouterror(inode, &data->args.errors[0].stateid,
99062306a36Sopenharmony_ci			       task->tk_status);
99162306a36Sopenharmony_ci}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_cistatic void
99462306a36Sopenharmony_cinfs42_layouterror_release(void *calldata)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	struct nfs42_layouterror_data *data = calldata;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	nfs42_free_layouterror_data(data);
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic const struct rpc_call_ops nfs42_layouterror_ops = {
100262306a36Sopenharmony_ci	.rpc_call_prepare = nfs42_layouterror_prepare,
100362306a36Sopenharmony_ci	.rpc_call_done = nfs42_layouterror_done,
100462306a36Sopenharmony_ci	.rpc_release = nfs42_layouterror_release,
100562306a36Sopenharmony_ci};
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ciint nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
100862306a36Sopenharmony_ci		const struct nfs42_layout_error *errors, size_t n)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	struct inode *inode = lseg->pls_layout->plh_inode;
101162306a36Sopenharmony_ci	struct nfs42_layouterror_data *data;
101262306a36Sopenharmony_ci	struct rpc_task *task;
101362306a36Sopenharmony_ci	struct rpc_message msg = {
101462306a36Sopenharmony_ci		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTERROR],
101562306a36Sopenharmony_ci	};
101662306a36Sopenharmony_ci	struct rpc_task_setup task_setup = {
101762306a36Sopenharmony_ci		.rpc_message = &msg,
101862306a36Sopenharmony_ci		.callback_ops = &nfs42_layouterror_ops,
101962306a36Sopenharmony_ci		.flags = RPC_TASK_ASYNC,
102062306a36Sopenharmony_ci	};
102162306a36Sopenharmony_ci	unsigned int i;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (!nfs_server_capable(inode, NFS_CAP_LAYOUTERROR))
102462306a36Sopenharmony_ci		return -EOPNOTSUPP;
102562306a36Sopenharmony_ci	if (n > NFS42_LAYOUTERROR_MAX)
102662306a36Sopenharmony_ci		return -EINVAL;
102762306a36Sopenharmony_ci	data = nfs42_alloc_layouterror_data(lseg, nfs_io_gfp_mask());
102862306a36Sopenharmony_ci	if (!data)
102962306a36Sopenharmony_ci		return -ENOMEM;
103062306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
103162306a36Sopenharmony_ci		data->args.errors[i] = errors[i];
103262306a36Sopenharmony_ci		data->args.num_errors++;
103362306a36Sopenharmony_ci		data->res.num_errors++;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci	msg.rpc_argp = &data->args;
103662306a36Sopenharmony_ci	msg.rpc_resp = &data->res;
103762306a36Sopenharmony_ci	task_setup.callback_data = data;
103862306a36Sopenharmony_ci	task_setup.rpc_client = NFS_SERVER(inode)->client;
103962306a36Sopenharmony_ci	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
104062306a36Sopenharmony_ci	task = rpc_run_task(&task_setup);
104162306a36Sopenharmony_ci	if (IS_ERR(task))
104262306a36Sopenharmony_ci		return PTR_ERR(task);
104362306a36Sopenharmony_ci	rpc_put_task(task);
104462306a36Sopenharmony_ci	return 0;
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs42_proc_layouterror);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_cistatic int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
104962306a36Sopenharmony_ci		struct file *dst_f, struct nfs_lock_context *src_lock,
105062306a36Sopenharmony_ci		struct nfs_lock_context *dst_lock, loff_t src_offset,
105162306a36Sopenharmony_ci		loff_t dst_offset, loff_t count)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	struct inode *src_inode = file_inode(src_f);
105462306a36Sopenharmony_ci	struct inode *dst_inode = file_inode(dst_f);
105562306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(dst_inode);
105662306a36Sopenharmony_ci	__u32 dst_bitmask[NFS_BITMASK_SZ];
105762306a36Sopenharmony_ci	struct nfs42_clone_args args = {
105862306a36Sopenharmony_ci		.src_fh = NFS_FH(src_inode),
105962306a36Sopenharmony_ci		.dst_fh = NFS_FH(dst_inode),
106062306a36Sopenharmony_ci		.src_offset = src_offset,
106162306a36Sopenharmony_ci		.dst_offset = dst_offset,
106262306a36Sopenharmony_ci		.count = count,
106362306a36Sopenharmony_ci		.dst_bitmask = dst_bitmask,
106462306a36Sopenharmony_ci	};
106562306a36Sopenharmony_ci	struct nfs42_clone_res res = {
106662306a36Sopenharmony_ci		.server	= server,
106762306a36Sopenharmony_ci	};
106862306a36Sopenharmony_ci	int status;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	msg->rpc_argp = &args;
107162306a36Sopenharmony_ci	msg->rpc_resp = &res;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context,
107462306a36Sopenharmony_ci			src_lock, FMODE_READ);
107562306a36Sopenharmony_ci	if (status) {
107662306a36Sopenharmony_ci		if (status == -EAGAIN)
107762306a36Sopenharmony_ci			status = -NFS4ERR_BAD_STATEID;
107862306a36Sopenharmony_ci		return status;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci	status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context,
108162306a36Sopenharmony_ci			dst_lock, FMODE_WRITE);
108262306a36Sopenharmony_ci	if (status) {
108362306a36Sopenharmony_ci		if (status == -EAGAIN)
108462306a36Sopenharmony_ci			status = -NFS4ERR_BAD_STATEID;
108562306a36Sopenharmony_ci		return status;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	res.dst_fattr = nfs_alloc_fattr();
108962306a36Sopenharmony_ci	if (!res.dst_fattr)
109062306a36Sopenharmony_ci		return -ENOMEM;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	nfs4_bitmask_set(dst_bitmask, server->cache_consistency_bitmask,
109362306a36Sopenharmony_ci			 dst_inode, NFS_INO_INVALID_BLOCKS);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	status = nfs4_call_sync(server->client, server, msg,
109662306a36Sopenharmony_ci				&args.seq_args, &res.seq_res, 0);
109762306a36Sopenharmony_ci	trace_nfs4_clone(src_inode, dst_inode, &args, status);
109862306a36Sopenharmony_ci	if (status == 0) {
109962306a36Sopenharmony_ci		/* a zero-length count means clone to EOF in src */
110062306a36Sopenharmony_ci		if (count == 0 && res.dst_fattr->valid & NFS_ATTR_FATTR_SIZE)
110162306a36Sopenharmony_ci			count = nfs_size_to_loff_t(res.dst_fattr->size) - dst_offset;
110262306a36Sopenharmony_ci		nfs42_copy_dest_done(dst_inode, dst_offset, count);
110362306a36Sopenharmony_ci		status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	kfree(res.dst_fattr);
110762306a36Sopenharmony_ci	return status;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ciint nfs42_proc_clone(struct file *src_f, struct file *dst_f,
111162306a36Sopenharmony_ci		     loff_t src_offset, loff_t dst_offset, loff_t count)
111262306a36Sopenharmony_ci{
111362306a36Sopenharmony_ci	struct rpc_message msg = {
111462306a36Sopenharmony_ci		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE],
111562306a36Sopenharmony_ci	};
111662306a36Sopenharmony_ci	struct inode *inode = file_inode(src_f);
111762306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(file_inode(src_f));
111862306a36Sopenharmony_ci	struct nfs_lock_context *src_lock;
111962306a36Sopenharmony_ci	struct nfs_lock_context *dst_lock;
112062306a36Sopenharmony_ci	struct nfs4_exception src_exception = { };
112162306a36Sopenharmony_ci	struct nfs4_exception dst_exception = { };
112262306a36Sopenharmony_ci	int err, err2;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	if (!nfs_server_capable(inode, NFS_CAP_CLONE))
112562306a36Sopenharmony_ci		return -EOPNOTSUPP;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	src_lock = nfs_get_lock_context(nfs_file_open_context(src_f));
112862306a36Sopenharmony_ci	if (IS_ERR(src_lock))
112962306a36Sopenharmony_ci		return PTR_ERR(src_lock);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	src_exception.inode = file_inode(src_f);
113262306a36Sopenharmony_ci	src_exception.state = src_lock->open_context->state;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f));
113562306a36Sopenharmony_ci	if (IS_ERR(dst_lock)) {
113662306a36Sopenharmony_ci		err = PTR_ERR(dst_lock);
113762306a36Sopenharmony_ci		goto out_put_src_lock;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	dst_exception.inode = file_inode(dst_f);
114162306a36Sopenharmony_ci	dst_exception.state = dst_lock->open_context->state;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	do {
114462306a36Sopenharmony_ci		err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock,
114562306a36Sopenharmony_ci					src_offset, dst_offset, count);
114662306a36Sopenharmony_ci		if (err == -ENOTSUPP || err == -EOPNOTSUPP) {
114762306a36Sopenharmony_ci			NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE;
114862306a36Sopenharmony_ci			err = -EOPNOTSUPP;
114962306a36Sopenharmony_ci			break;
115062306a36Sopenharmony_ci		}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci		err2 = nfs4_handle_exception(server, err, &src_exception);
115362306a36Sopenharmony_ci		err = nfs4_handle_exception(server, err, &dst_exception);
115462306a36Sopenharmony_ci		if (!err)
115562306a36Sopenharmony_ci			err = err2;
115662306a36Sopenharmony_ci	} while (src_exception.retry || dst_exception.retry);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	nfs_put_lock_context(dst_lock);
115962306a36Sopenharmony_ciout_put_src_lock:
116062306a36Sopenharmony_ci	nfs_put_lock_context(src_lock);
116162306a36Sopenharmony_ci	return err;
116262306a36Sopenharmony_ci}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci#define NFS4XATTR_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_cistatic int _nfs42_proc_removexattr(struct inode *inode, const char *name)
116762306a36Sopenharmony_ci{
116862306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
116962306a36Sopenharmony_ci	struct nfs42_removexattrargs args = {
117062306a36Sopenharmony_ci		.fh = NFS_FH(inode),
117162306a36Sopenharmony_ci		.xattr_name = name,
117262306a36Sopenharmony_ci	};
117362306a36Sopenharmony_ci	struct nfs42_removexattrres res;
117462306a36Sopenharmony_ci	struct rpc_message msg = {
117562306a36Sopenharmony_ci		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVEXATTR],
117662306a36Sopenharmony_ci		.rpc_argp = &args,
117762306a36Sopenharmony_ci		.rpc_resp = &res,
117862306a36Sopenharmony_ci	};
117962306a36Sopenharmony_ci	int ret;
118062306a36Sopenharmony_ci	unsigned long timestamp = jiffies;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	ret = nfs4_call_sync(server->client, server, &msg, &args.seq_args,
118362306a36Sopenharmony_ci	    &res.seq_res, 1);
118462306a36Sopenharmony_ci	trace_nfs4_removexattr(inode, name, ret);
118562306a36Sopenharmony_ci	if (!ret)
118662306a36Sopenharmony_ci		nfs4_update_changeattr(inode, &res.cinfo, timestamp, 0);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	return ret;
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic int _nfs42_proc_setxattr(struct inode *inode, const char *name,
119262306a36Sopenharmony_ci				const void *buf, size_t buflen, int flags)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
119562306a36Sopenharmony_ci	__u32 bitmask[NFS_BITMASK_SZ];
119662306a36Sopenharmony_ci	struct page *pages[NFS4XATTR_MAXPAGES];
119762306a36Sopenharmony_ci	struct nfs42_setxattrargs arg = {
119862306a36Sopenharmony_ci		.fh		= NFS_FH(inode),
119962306a36Sopenharmony_ci		.bitmask	= bitmask,
120062306a36Sopenharmony_ci		.xattr_pages	= pages,
120162306a36Sopenharmony_ci		.xattr_len	= buflen,
120262306a36Sopenharmony_ci		.xattr_name	= name,
120362306a36Sopenharmony_ci		.xattr_flags	= flags,
120462306a36Sopenharmony_ci	};
120562306a36Sopenharmony_ci	struct nfs42_setxattrres res = {
120662306a36Sopenharmony_ci		.server		= server,
120762306a36Sopenharmony_ci	};
120862306a36Sopenharmony_ci	struct rpc_message msg = {
120962306a36Sopenharmony_ci		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_SETXATTR],
121062306a36Sopenharmony_ci		.rpc_argp	= &arg,
121162306a36Sopenharmony_ci		.rpc_resp	= &res,
121262306a36Sopenharmony_ci	};
121362306a36Sopenharmony_ci	int ret, np;
121462306a36Sopenharmony_ci	unsigned long timestamp = jiffies;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	if (buflen > server->sxasize)
121762306a36Sopenharmony_ci		return -ERANGE;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	res.fattr = nfs_alloc_fattr();
122062306a36Sopenharmony_ci	if (!res.fattr)
122162306a36Sopenharmony_ci		return -ENOMEM;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	if (buflen > 0) {
122462306a36Sopenharmony_ci		np = nfs4_buf_to_pages_noslab(buf, buflen, arg.xattr_pages);
122562306a36Sopenharmony_ci		if (np < 0) {
122662306a36Sopenharmony_ci			ret = np;
122762306a36Sopenharmony_ci			goto out;
122862306a36Sopenharmony_ci		}
122962306a36Sopenharmony_ci	} else
123062306a36Sopenharmony_ci		np = 0;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	nfs4_bitmask_set(bitmask, server->cache_consistency_bitmask,
123362306a36Sopenharmony_ci			 inode, NFS_INO_INVALID_CHANGE);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
123662306a36Sopenharmony_ci	    &res.seq_res, 1);
123762306a36Sopenharmony_ci	trace_nfs4_setxattr(inode, name, ret);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	for (; np > 0; np--)
124062306a36Sopenharmony_ci		put_page(pages[np - 1]);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	if (!ret) {
124362306a36Sopenharmony_ci		nfs4_update_changeattr(inode, &res.cinfo, timestamp, 0);
124462306a36Sopenharmony_ci		ret = nfs_post_op_update_inode(inode, res.fattr);
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ciout:
124862306a36Sopenharmony_ci	kfree(res.fattr);
124962306a36Sopenharmony_ci	return ret;
125062306a36Sopenharmony_ci}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic ssize_t _nfs42_proc_getxattr(struct inode *inode, const char *name,
125362306a36Sopenharmony_ci				void *buf, size_t buflen, struct page **pages,
125462306a36Sopenharmony_ci				size_t plen)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
125762306a36Sopenharmony_ci	struct nfs42_getxattrargs arg = {
125862306a36Sopenharmony_ci		.fh		= NFS_FH(inode),
125962306a36Sopenharmony_ci		.xattr_name	= name,
126062306a36Sopenharmony_ci	};
126162306a36Sopenharmony_ci	struct nfs42_getxattrres res;
126262306a36Sopenharmony_ci	struct rpc_message msg = {
126362306a36Sopenharmony_ci		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_GETXATTR],
126462306a36Sopenharmony_ci		.rpc_argp	= &arg,
126562306a36Sopenharmony_ci		.rpc_resp	= &res,
126662306a36Sopenharmony_ci	};
126762306a36Sopenharmony_ci	ssize_t ret;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	arg.xattr_len = plen;
127062306a36Sopenharmony_ci	arg.xattr_pages = pages;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
127362306a36Sopenharmony_ci	    &res.seq_res, 0);
127462306a36Sopenharmony_ci	trace_nfs4_getxattr(inode, name, ret);
127562306a36Sopenharmony_ci	if (ret < 0)
127662306a36Sopenharmony_ci		return ret;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	/*
127962306a36Sopenharmony_ci	 * Normally, the caching is done one layer up, but for successful
128062306a36Sopenharmony_ci	 * RPCS, always cache the result here, even if the caller was
128162306a36Sopenharmony_ci	 * just querying the length, or if the reply was too big for
128262306a36Sopenharmony_ci	 * the caller. This avoids a second RPC in the case of the
128362306a36Sopenharmony_ci	 * common query-alloc-retrieve cycle for xattrs.
128462306a36Sopenharmony_ci	 *
128562306a36Sopenharmony_ci	 * Note that xattr_len is always capped to XATTR_SIZE_MAX.
128662306a36Sopenharmony_ci	 */
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	nfs4_xattr_cache_add(inode, name, NULL, pages, res.xattr_len);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	if (buflen) {
129162306a36Sopenharmony_ci		if (res.xattr_len > buflen)
129262306a36Sopenharmony_ci			return -ERANGE;
129362306a36Sopenharmony_ci		_copy_from_pages(buf, pages, 0, res.xattr_len);
129462306a36Sopenharmony_ci	}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	return res.xattr_len;
129762306a36Sopenharmony_ci}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_cistatic ssize_t _nfs42_proc_listxattrs(struct inode *inode, void *buf,
130062306a36Sopenharmony_ci				 size_t buflen, u64 *cookiep, bool *eofp)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
130362306a36Sopenharmony_ci	struct page **pages;
130462306a36Sopenharmony_ci	struct nfs42_listxattrsargs arg = {
130562306a36Sopenharmony_ci		.fh		= NFS_FH(inode),
130662306a36Sopenharmony_ci		.cookie		= *cookiep,
130762306a36Sopenharmony_ci	};
130862306a36Sopenharmony_ci	struct nfs42_listxattrsres res = {
130962306a36Sopenharmony_ci		.eof = false,
131062306a36Sopenharmony_ci		.xattr_buf = buf,
131162306a36Sopenharmony_ci		.xattr_len = buflen,
131262306a36Sopenharmony_ci	};
131362306a36Sopenharmony_ci	struct rpc_message msg = {
131462306a36Sopenharmony_ci		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_LISTXATTRS],
131562306a36Sopenharmony_ci		.rpc_argp	= &arg,
131662306a36Sopenharmony_ci		.rpc_resp	= &res,
131762306a36Sopenharmony_ci	};
131862306a36Sopenharmony_ci	u32 xdrlen;
131962306a36Sopenharmony_ci	int ret, np, i;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	ret = -ENOMEM;
132362306a36Sopenharmony_ci	res.scratch = alloc_page(GFP_KERNEL);
132462306a36Sopenharmony_ci	if (!res.scratch)
132562306a36Sopenharmony_ci		goto out;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	xdrlen = nfs42_listxattr_xdrsize(buflen);
132862306a36Sopenharmony_ci	if (xdrlen > server->lxasize)
132962306a36Sopenharmony_ci		xdrlen = server->lxasize;
133062306a36Sopenharmony_ci	np = xdrlen / PAGE_SIZE + 1;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	pages = kcalloc(np, sizeof(struct page *), GFP_KERNEL);
133362306a36Sopenharmony_ci	if (!pages)
133462306a36Sopenharmony_ci		goto out_free_scratch;
133562306a36Sopenharmony_ci	for (i = 0; i < np; i++) {
133662306a36Sopenharmony_ci		pages[i] = alloc_page(GFP_KERNEL);
133762306a36Sopenharmony_ci		if (!pages[i])
133862306a36Sopenharmony_ci			goto out_free_pages;
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	arg.xattr_pages = pages;
134262306a36Sopenharmony_ci	arg.count = xdrlen;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args,
134562306a36Sopenharmony_ci	    &res.seq_res, 0);
134662306a36Sopenharmony_ci	trace_nfs4_listxattr(inode, ret);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	if (ret >= 0) {
134962306a36Sopenharmony_ci		ret = res.copied;
135062306a36Sopenharmony_ci		*cookiep = res.cookie;
135162306a36Sopenharmony_ci		*eofp = res.eof;
135262306a36Sopenharmony_ci	}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ciout_free_pages:
135562306a36Sopenharmony_ci	while (--np >= 0) {
135662306a36Sopenharmony_ci		if (pages[np])
135762306a36Sopenharmony_ci			__free_page(pages[np]);
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci	kfree(pages);
136062306a36Sopenharmony_ciout_free_scratch:
136162306a36Sopenharmony_ci	__free_page(res.scratch);
136262306a36Sopenharmony_ciout:
136362306a36Sopenharmony_ci	return ret;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cissize_t nfs42_proc_getxattr(struct inode *inode, const char *name,
136862306a36Sopenharmony_ci			      void *buf, size_t buflen)
136962306a36Sopenharmony_ci{
137062306a36Sopenharmony_ci	struct nfs4_exception exception = { };
137162306a36Sopenharmony_ci	ssize_t err, np, i;
137262306a36Sopenharmony_ci	struct page **pages;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	np = nfs_page_array_len(0, buflen ?: XATTR_SIZE_MAX);
137562306a36Sopenharmony_ci	pages = kmalloc_array(np, sizeof(*pages), GFP_KERNEL);
137662306a36Sopenharmony_ci	if (!pages)
137762306a36Sopenharmony_ci		return -ENOMEM;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	for (i = 0; i < np; i++) {
138062306a36Sopenharmony_ci		pages[i] = alloc_page(GFP_KERNEL);
138162306a36Sopenharmony_ci		if (!pages[i]) {
138262306a36Sopenharmony_ci			err = -ENOMEM;
138362306a36Sopenharmony_ci			goto out;
138462306a36Sopenharmony_ci		}
138562306a36Sopenharmony_ci	}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	/*
138862306a36Sopenharmony_ci	 * The GETXATTR op has no length field in the call, and the
138962306a36Sopenharmony_ci	 * xattr data is at the end of the reply.
139062306a36Sopenharmony_ci	 *
139162306a36Sopenharmony_ci	 * There is no downside in using the page-aligned length. It will
139262306a36Sopenharmony_ci	 * allow receiving and caching xattrs that are too large for the
139362306a36Sopenharmony_ci	 * caller but still fit in the page-rounded value.
139462306a36Sopenharmony_ci	 */
139562306a36Sopenharmony_ci	do {
139662306a36Sopenharmony_ci		err = _nfs42_proc_getxattr(inode, name, buf, buflen,
139762306a36Sopenharmony_ci			pages, np * PAGE_SIZE);
139862306a36Sopenharmony_ci		if (err >= 0)
139962306a36Sopenharmony_ci			break;
140062306a36Sopenharmony_ci		err = nfs4_handle_exception(NFS_SERVER(inode), err,
140162306a36Sopenharmony_ci				&exception);
140262306a36Sopenharmony_ci	} while (exception.retry);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ciout:
140562306a36Sopenharmony_ci	while (--i >= 0)
140662306a36Sopenharmony_ci		__free_page(pages[i]);
140762306a36Sopenharmony_ci	kfree(pages);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	return err;
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ciint nfs42_proc_setxattr(struct inode *inode, const char *name,
141362306a36Sopenharmony_ci			      const void *buf, size_t buflen, int flags)
141462306a36Sopenharmony_ci{
141562306a36Sopenharmony_ci	struct nfs4_exception exception = { };
141662306a36Sopenharmony_ci	int err;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	do {
141962306a36Sopenharmony_ci		err = _nfs42_proc_setxattr(inode, name, buf, buflen, flags);
142062306a36Sopenharmony_ci		if (!err)
142162306a36Sopenharmony_ci			break;
142262306a36Sopenharmony_ci		err = nfs4_handle_exception(NFS_SERVER(inode), err,
142362306a36Sopenharmony_ci				&exception);
142462306a36Sopenharmony_ci	} while (exception.retry);
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	return err;
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_cissize_t nfs42_proc_listxattrs(struct inode *inode, void *buf,
143062306a36Sopenharmony_ci			      size_t buflen, u64 *cookiep, bool *eofp)
143162306a36Sopenharmony_ci{
143262306a36Sopenharmony_ci	struct nfs4_exception exception = { };
143362306a36Sopenharmony_ci	ssize_t err;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	do {
143662306a36Sopenharmony_ci		err = _nfs42_proc_listxattrs(inode, buf, buflen,
143762306a36Sopenharmony_ci		    cookiep, eofp);
143862306a36Sopenharmony_ci		if (err >= 0)
143962306a36Sopenharmony_ci			break;
144062306a36Sopenharmony_ci		err = nfs4_handle_exception(NFS_SERVER(inode), err,
144162306a36Sopenharmony_ci				&exception);
144262306a36Sopenharmony_ci	} while (exception.retry);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	return err;
144562306a36Sopenharmony_ci}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ciint nfs42_proc_removexattr(struct inode *inode, const char *name)
144862306a36Sopenharmony_ci{
144962306a36Sopenharmony_ci	struct nfs4_exception exception = { };
145062306a36Sopenharmony_ci	int err;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	do {
145362306a36Sopenharmony_ci		err = _nfs42_proc_removexattr(inode, name);
145462306a36Sopenharmony_ci		if (!err)
145562306a36Sopenharmony_ci			break;
145662306a36Sopenharmony_ci		err = nfs4_handle_exception(NFS_SERVER(inode), err,
145762306a36Sopenharmony_ci				&exception);
145862306a36Sopenharmony_ci	} while (exception.retry);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	return err;
146162306a36Sopenharmony_ci}
1462