162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  fs/nfs/nfs4state.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Client-side XDR for NFSv4.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Copyright (c) 2002 The Regents of the University of Michigan.
762306a36Sopenharmony_ci *  All rights reserved.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  Kendrick Smith <kmsmith@umich.edu>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *  Redistribution and use in source and binary forms, with or without
1262306a36Sopenharmony_ci *  modification, are permitted provided that the following conditions
1362306a36Sopenharmony_ci *  are met:
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *  1. Redistributions of source code must retain the above copyright
1662306a36Sopenharmony_ci *     notice, this list of conditions and the following disclaimer.
1762306a36Sopenharmony_ci *  2. Redistributions in binary form must reproduce the above copyright
1862306a36Sopenharmony_ci *     notice, this list of conditions and the following disclaimer in the
1962306a36Sopenharmony_ci *     documentation and/or other materials provided with the distribution.
2062306a36Sopenharmony_ci *  3. Neither the name of the University nor the names of its
2162306a36Sopenharmony_ci *     contributors may be used to endorse or promote products derived
2262306a36Sopenharmony_ci *     from this software without specific prior written permission.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
2562306a36Sopenharmony_ci *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2662306a36Sopenharmony_ci *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2762306a36Sopenharmony_ci *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2862306a36Sopenharmony_ci *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2962306a36Sopenharmony_ci *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3062306a36Sopenharmony_ci *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
3162306a36Sopenharmony_ci *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
3262306a36Sopenharmony_ci *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3362306a36Sopenharmony_ci *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3462306a36Sopenharmony_ci *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * Implementation of the NFSv4 state model.  For the time being,
3762306a36Sopenharmony_ci * this is minimal, but will be made much more complex in a
3862306a36Sopenharmony_ci * subsequent patch.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <linux/kernel.h>
4262306a36Sopenharmony_ci#include <linux/slab.h>
4362306a36Sopenharmony_ci#include <linux/fs.h>
4462306a36Sopenharmony_ci#include <linux/nfs_fs.h>
4562306a36Sopenharmony_ci#include <linux/kthread.h>
4662306a36Sopenharmony_ci#include <linux/module.h>
4762306a36Sopenharmony_ci#include <linux/random.h>
4862306a36Sopenharmony_ci#include <linux/ratelimit.h>
4962306a36Sopenharmony_ci#include <linux/workqueue.h>
5062306a36Sopenharmony_ci#include <linux/bitops.h>
5162306a36Sopenharmony_ci#include <linux/jiffies.h>
5262306a36Sopenharmony_ci#include <linux/sched/mm.h>
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h>
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#include "nfs4_fs.h"
5762306a36Sopenharmony_ci#include "callback.h"
5862306a36Sopenharmony_ci#include "delegation.h"
5962306a36Sopenharmony_ci#include "internal.h"
6062306a36Sopenharmony_ci#include "nfs4idmap.h"
6162306a36Sopenharmony_ci#include "nfs4session.h"
6262306a36Sopenharmony_ci#include "pnfs.h"
6362306a36Sopenharmony_ci#include "netns.h"
6462306a36Sopenharmony_ci#include "nfs4trace.h"
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_STATE
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define OPENOWNER_POOL_SIZE	8
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void nfs4_state_start_reclaim_reboot(struct nfs_client *clp);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciconst nfs4_stateid zero_stateid = {
7362306a36Sopenharmony_ci	{ .data = { 0 } },
7462306a36Sopenharmony_ci	.type = NFS4_SPECIAL_STATEID_TYPE,
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ciconst nfs4_stateid invalid_stateid = {
7762306a36Sopenharmony_ci	{
7862306a36Sopenharmony_ci		/* Funky initialiser keeps older gcc versions happy */
7962306a36Sopenharmony_ci		.data = { 0xff, 0xff, 0xff, 0xff, 0 },
8062306a36Sopenharmony_ci	},
8162306a36Sopenharmony_ci	.type = NFS4_INVALID_STATEID_TYPE,
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ciconst nfs4_stateid current_stateid = {
8562306a36Sopenharmony_ci	{
8662306a36Sopenharmony_ci		/* Funky initialiser keeps older gcc versions happy */
8762306a36Sopenharmony_ci		.data = { 0x0, 0x0, 0x0, 0x1, 0 },
8862306a36Sopenharmony_ci	},
8962306a36Sopenharmony_ci	.type = NFS4_SPECIAL_STATEID_TYPE,
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic DEFINE_MUTEX(nfs_clid_init_mutex);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int nfs4_setup_state_renewal(struct nfs_client *clp)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int status;
9762306a36Sopenharmony_ci	struct nfs_fsinfo fsinfo;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) {
10062306a36Sopenharmony_ci		nfs4_schedule_state_renewal(clp);
10162306a36Sopenharmony_ci		return 0;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	status = nfs4_proc_get_lease_time(clp, &fsinfo);
10562306a36Sopenharmony_ci	if (status == 0) {
10662306a36Sopenharmony_ci		nfs4_set_lease_period(clp, fsinfo.lease_time * HZ);
10762306a36Sopenharmony_ci		nfs4_schedule_state_renewal(clp);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return status;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciint nfs4_init_clientid(struct nfs_client *clp, const struct cred *cred)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct nfs4_setclientid_res clid = {
11662306a36Sopenharmony_ci		.clientid = clp->cl_clientid,
11762306a36Sopenharmony_ci		.confirm = clp->cl_confirm,
11862306a36Sopenharmony_ci	};
11962306a36Sopenharmony_ci	unsigned short port;
12062306a36Sopenharmony_ci	int status;
12162306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
12462306a36Sopenharmony_ci		goto do_confirm;
12562306a36Sopenharmony_ci	port = nn->nfs_callback_tcpport;
12662306a36Sopenharmony_ci	if (clp->cl_addr.ss_family == AF_INET6)
12762306a36Sopenharmony_ci		port = nn->nfs_callback_tcpport6;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
13062306a36Sopenharmony_ci	if (status != 0)
13162306a36Sopenharmony_ci		goto out;
13262306a36Sopenharmony_ci	clp->cl_clientid = clid.clientid;
13362306a36Sopenharmony_ci	clp->cl_confirm = clid.confirm;
13462306a36Sopenharmony_ci	set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
13562306a36Sopenharmony_cido_confirm:
13662306a36Sopenharmony_ci	status = nfs4_proc_setclientid_confirm(clp, &clid, cred);
13762306a36Sopenharmony_ci	if (status != 0)
13862306a36Sopenharmony_ci		goto out;
13962306a36Sopenharmony_ci	clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
14062306a36Sopenharmony_ci	nfs4_setup_state_renewal(clp);
14162306a36Sopenharmony_ciout:
14262306a36Sopenharmony_ci	return status;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/**
14662306a36Sopenharmony_ci * nfs40_discover_server_trunking - Detect server IP address trunking (mv0)
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci * @clp: nfs_client under test
14962306a36Sopenharmony_ci * @result: OUT: found nfs_client, or clp
15062306a36Sopenharmony_ci * @cred: credential to use for trunking test
15162306a36Sopenharmony_ci *
15262306a36Sopenharmony_ci * Returns zero, a negative errno, or a negative NFS4ERR status.
15362306a36Sopenharmony_ci * If zero is returned, an nfs_client pointer is planted in
15462306a36Sopenharmony_ci * "result".
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci * Note: The returned client may not yet be marked ready.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_ciint nfs40_discover_server_trunking(struct nfs_client *clp,
15962306a36Sopenharmony_ci				   struct nfs_client **result,
16062306a36Sopenharmony_ci				   const struct cred *cred)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct nfs4_setclientid_res clid = {
16362306a36Sopenharmony_ci		.clientid = clp->cl_clientid,
16462306a36Sopenharmony_ci		.confirm = clp->cl_confirm,
16562306a36Sopenharmony_ci	};
16662306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
16762306a36Sopenharmony_ci	unsigned short port;
16862306a36Sopenharmony_ci	int status;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	port = nn->nfs_callback_tcpport;
17162306a36Sopenharmony_ci	if (clp->cl_addr.ss_family == AF_INET6)
17262306a36Sopenharmony_ci		port = nn->nfs_callback_tcpport6;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
17562306a36Sopenharmony_ci	if (status != 0)
17662306a36Sopenharmony_ci		goto out;
17762306a36Sopenharmony_ci	clp->cl_clientid = clid.clientid;
17862306a36Sopenharmony_ci	clp->cl_confirm = clid.confirm;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	status = nfs40_walk_client_list(clp, result, cred);
18162306a36Sopenharmony_ci	if (status == 0) {
18262306a36Sopenharmony_ci		/* Sustain the lease, even if it's empty.  If the clientid4
18362306a36Sopenharmony_ci		 * goes stale it's of no use for trunking discovery. */
18462306a36Sopenharmony_ci		nfs4_schedule_state_renewal(*result);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		/* If the client state need to recover, do it. */
18762306a36Sopenharmony_ci		if (clp->cl_state)
18862306a36Sopenharmony_ci			nfs4_schedule_state_manager(clp);
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ciout:
19162306a36Sopenharmony_ci	return status;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ciconst struct cred *nfs4_get_machine_cred(struct nfs_client *clp)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	return get_cred(rpc_machine_cred());
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void nfs4_root_machine_cred(struct nfs_client *clp)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* Force root creds instead of machine */
20362306a36Sopenharmony_ci	clp->cl_principal = NULL;
20462306a36Sopenharmony_ci	clp->cl_rpcclient->cl_principal = NULL;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic const struct cred *
20862306a36Sopenharmony_cinfs4_get_renew_cred_server_locked(struct nfs_server *server)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	const struct cred *cred = NULL;
21162306a36Sopenharmony_ci	struct nfs4_state_owner *sp;
21262306a36Sopenharmony_ci	struct rb_node *pos;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	for (pos = rb_first(&server->state_owners);
21562306a36Sopenharmony_ci	     pos != NULL;
21662306a36Sopenharmony_ci	     pos = rb_next(pos)) {
21762306a36Sopenharmony_ci		sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
21862306a36Sopenharmony_ci		if (list_empty(&sp->so_states))
21962306a36Sopenharmony_ci			continue;
22062306a36Sopenharmony_ci		cred = get_cred(sp->so_cred);
22162306a36Sopenharmony_ci		break;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci	return cred;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/**
22762306a36Sopenharmony_ci * nfs4_get_renew_cred - Acquire credential for a renew operation
22862306a36Sopenharmony_ci * @clp: client state handle
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * Returns an rpc_cred with reference count bumped, or NULL.
23162306a36Sopenharmony_ci * Caller must hold clp->cl_lock.
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_ciconst struct cred *nfs4_get_renew_cred(struct nfs_client *clp)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	const struct cred *cred = NULL;
23662306a36Sopenharmony_ci	struct nfs_server *server;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Use machine credentials if available */
23962306a36Sopenharmony_ci	cred = nfs4_get_machine_cred(clp);
24062306a36Sopenharmony_ci	if (cred != NULL)
24162306a36Sopenharmony_ci		goto out;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
24462306a36Sopenharmony_ci	rcu_read_lock();
24562306a36Sopenharmony_ci	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
24662306a36Sopenharmony_ci		cred = nfs4_get_renew_cred_server_locked(server);
24762306a36Sopenharmony_ci		if (cred != NULL)
24862306a36Sopenharmony_ci			break;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	rcu_read_unlock();
25162306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ciout:
25462306a36Sopenharmony_ci	return cred;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void nfs4_end_drain_slot_table(struct nfs4_slot_table *tbl)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	if (test_and_clear_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) {
26062306a36Sopenharmony_ci		spin_lock(&tbl->slot_tbl_lock);
26162306a36Sopenharmony_ci		nfs41_wake_slot_table(tbl);
26262306a36Sopenharmony_ci		spin_unlock(&tbl->slot_tbl_lock);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic void nfs4_end_drain_session(struct nfs_client *clp)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct nfs4_session *ses = clp->cl_session;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (clp->cl_slot_tbl) {
27162306a36Sopenharmony_ci		nfs4_end_drain_slot_table(clp->cl_slot_tbl);
27262306a36Sopenharmony_ci		return;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (ses != NULL) {
27662306a36Sopenharmony_ci		nfs4_end_drain_slot_table(&ses->bc_slot_table);
27762306a36Sopenharmony_ci		nfs4_end_drain_slot_table(&ses->fc_slot_table);
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic int nfs4_drain_slot_tbl(struct nfs4_slot_table *tbl)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	set_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state);
28462306a36Sopenharmony_ci	spin_lock(&tbl->slot_tbl_lock);
28562306a36Sopenharmony_ci	if (tbl->highest_used_slotid != NFS4_NO_SLOT) {
28662306a36Sopenharmony_ci		reinit_completion(&tbl->complete);
28762306a36Sopenharmony_ci		spin_unlock(&tbl->slot_tbl_lock);
28862306a36Sopenharmony_ci		return wait_for_completion_interruptible(&tbl->complete);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci	spin_unlock(&tbl->slot_tbl_lock);
29162306a36Sopenharmony_ci	return 0;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic int nfs4_begin_drain_session(struct nfs_client *clp)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct nfs4_session *ses = clp->cl_session;
29762306a36Sopenharmony_ci	int ret;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (clp->cl_slot_tbl)
30062306a36Sopenharmony_ci		return nfs4_drain_slot_tbl(clp->cl_slot_tbl);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* back channel */
30362306a36Sopenharmony_ci	ret = nfs4_drain_slot_tbl(&ses->bc_slot_table);
30462306a36Sopenharmony_ci	if (ret)
30562306a36Sopenharmony_ci		return ret;
30662306a36Sopenharmony_ci	/* fore channel */
30762306a36Sopenharmony_ci	return nfs4_drain_slot_tbl(&ses->fc_slot_table);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci#if defined(CONFIG_NFS_V4_1)
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic void nfs41_finish_session_reset(struct nfs_client *clp)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
31562306a36Sopenharmony_ci	clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
31662306a36Sopenharmony_ci	/* create_session negotiated new slot table */
31762306a36Sopenharmony_ci	clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
31862306a36Sopenharmony_ci	nfs4_setup_state_renewal(clp);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ciint nfs41_init_clientid(struct nfs_client *clp, const struct cred *cred)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	int status;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
32662306a36Sopenharmony_ci		goto do_confirm;
32762306a36Sopenharmony_ci	status = nfs4_proc_exchange_id(clp, cred);
32862306a36Sopenharmony_ci	if (status != 0)
32962306a36Sopenharmony_ci		goto out;
33062306a36Sopenharmony_ci	set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
33162306a36Sopenharmony_cido_confirm:
33262306a36Sopenharmony_ci	status = nfs4_proc_create_session(clp, cred);
33362306a36Sopenharmony_ci	if (status != 0)
33462306a36Sopenharmony_ci		goto out;
33562306a36Sopenharmony_ci	if (!(clp->cl_exchange_flags & EXCHGID4_FLAG_CONFIRMED_R))
33662306a36Sopenharmony_ci		nfs4_state_start_reclaim_reboot(clp);
33762306a36Sopenharmony_ci	nfs41_finish_session_reset(clp);
33862306a36Sopenharmony_ci	nfs_mark_client_ready(clp, NFS_CS_READY);
33962306a36Sopenharmony_ciout:
34062306a36Sopenharmony_ci	return status;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci/**
34462306a36Sopenharmony_ci * nfs41_discover_server_trunking - Detect server IP address trunking (mv1)
34562306a36Sopenharmony_ci *
34662306a36Sopenharmony_ci * @clp: nfs_client under test
34762306a36Sopenharmony_ci * @result: OUT: found nfs_client, or clp
34862306a36Sopenharmony_ci * @cred: credential to use for trunking test
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci * Returns NFS4_OK, a negative errno, or a negative NFS4ERR status.
35162306a36Sopenharmony_ci * If NFS4_OK is returned, an nfs_client pointer is planted in
35262306a36Sopenharmony_ci * "result".
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * Note: The returned client may not yet be marked ready.
35562306a36Sopenharmony_ci */
35662306a36Sopenharmony_ciint nfs41_discover_server_trunking(struct nfs_client *clp,
35762306a36Sopenharmony_ci				   struct nfs_client **result,
35862306a36Sopenharmony_ci				   const struct cred *cred)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	int status;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	status = nfs4_proc_exchange_id(clp, cred);
36362306a36Sopenharmony_ci	if (status != NFS4_OK)
36462306a36Sopenharmony_ci		return status;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	status = nfs41_walk_client_list(clp, result, cred);
36762306a36Sopenharmony_ci	if (status < 0)
36862306a36Sopenharmony_ci		return status;
36962306a36Sopenharmony_ci	if (clp != *result)
37062306a36Sopenharmony_ci		return 0;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	/*
37362306a36Sopenharmony_ci	 * Purge state if the client id was established in a prior
37462306a36Sopenharmony_ci	 * instance and the client id could not have arrived on the
37562306a36Sopenharmony_ci	 * server via Transparent State Migration.
37662306a36Sopenharmony_ci	 */
37762306a36Sopenharmony_ci	if (clp->cl_exchange_flags & EXCHGID4_FLAG_CONFIRMED_R) {
37862306a36Sopenharmony_ci		if (!test_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags))
37962306a36Sopenharmony_ci			set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
38062306a36Sopenharmony_ci		else
38162306a36Sopenharmony_ci			set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci	nfs4_schedule_state_manager(clp);
38462306a36Sopenharmony_ci	status = nfs_wait_client_init_complete(clp);
38562306a36Sopenharmony_ci	if (status < 0)
38662306a36Sopenharmony_ci		nfs_put_client(clp);
38762306a36Sopenharmony_ci	return status;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci/**
39362306a36Sopenharmony_ci * nfs4_get_clid_cred - Acquire credential for a setclientid operation
39462306a36Sopenharmony_ci * @clp: client state handle
39562306a36Sopenharmony_ci *
39662306a36Sopenharmony_ci * Returns a cred with reference count bumped, or NULL.
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_ciconst struct cred *nfs4_get_clid_cred(struct nfs_client *clp)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	const struct cred *cred;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	cred = nfs4_get_machine_cred(clp);
40362306a36Sopenharmony_ci	return cred;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic struct nfs4_state_owner *
40762306a36Sopenharmony_cinfs4_find_state_owner_locked(struct nfs_server *server, const struct cred *cred)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct rb_node **p = &server->state_owners.rb_node,
41062306a36Sopenharmony_ci		       *parent = NULL;
41162306a36Sopenharmony_ci	struct nfs4_state_owner *sp;
41262306a36Sopenharmony_ci	int cmp;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	while (*p != NULL) {
41562306a36Sopenharmony_ci		parent = *p;
41662306a36Sopenharmony_ci		sp = rb_entry(parent, struct nfs4_state_owner, so_server_node);
41762306a36Sopenharmony_ci		cmp = cred_fscmp(cred, sp->so_cred);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		if (cmp < 0)
42062306a36Sopenharmony_ci			p = &parent->rb_left;
42162306a36Sopenharmony_ci		else if (cmp > 0)
42262306a36Sopenharmony_ci			p = &parent->rb_right;
42362306a36Sopenharmony_ci		else {
42462306a36Sopenharmony_ci			if (!list_empty(&sp->so_lru))
42562306a36Sopenharmony_ci				list_del_init(&sp->so_lru);
42662306a36Sopenharmony_ci			atomic_inc(&sp->so_count);
42762306a36Sopenharmony_ci			return sp;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci	return NULL;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic struct nfs4_state_owner *
43462306a36Sopenharmony_cinfs4_insert_state_owner_locked(struct nfs4_state_owner *new)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct nfs_server *server = new->so_server;
43762306a36Sopenharmony_ci	struct rb_node **p = &server->state_owners.rb_node,
43862306a36Sopenharmony_ci		       *parent = NULL;
43962306a36Sopenharmony_ci	struct nfs4_state_owner *sp;
44062306a36Sopenharmony_ci	int cmp;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	while (*p != NULL) {
44362306a36Sopenharmony_ci		parent = *p;
44462306a36Sopenharmony_ci		sp = rb_entry(parent, struct nfs4_state_owner, so_server_node);
44562306a36Sopenharmony_ci		cmp = cred_fscmp(new->so_cred, sp->so_cred);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		if (cmp < 0)
44862306a36Sopenharmony_ci			p = &parent->rb_left;
44962306a36Sopenharmony_ci		else if (cmp > 0)
45062306a36Sopenharmony_ci			p = &parent->rb_right;
45162306a36Sopenharmony_ci		else {
45262306a36Sopenharmony_ci			if (!list_empty(&sp->so_lru))
45362306a36Sopenharmony_ci				list_del_init(&sp->so_lru);
45462306a36Sopenharmony_ci			atomic_inc(&sp->so_count);
45562306a36Sopenharmony_ci			return sp;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	rb_link_node(&new->so_server_node, parent, p);
45962306a36Sopenharmony_ci	rb_insert_color(&new->so_server_node, &server->state_owners);
46062306a36Sopenharmony_ci	return new;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic void
46462306a36Sopenharmony_cinfs4_remove_state_owner_locked(struct nfs4_state_owner *sp)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct nfs_server *server = sp->so_server;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (!RB_EMPTY_NODE(&sp->so_server_node))
46962306a36Sopenharmony_ci		rb_erase(&sp->so_server_node, &server->state_owners);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic void
47362306a36Sopenharmony_cinfs4_init_seqid_counter(struct nfs_seqid_counter *sc)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	sc->create_time = ktime_get();
47662306a36Sopenharmony_ci	sc->flags = 0;
47762306a36Sopenharmony_ci	sc->counter = 0;
47862306a36Sopenharmony_ci	spin_lock_init(&sc->lock);
47962306a36Sopenharmony_ci	INIT_LIST_HEAD(&sc->list);
48062306a36Sopenharmony_ci	rpc_init_wait_queue(&sc->wait, "Seqid_waitqueue");
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic void
48462306a36Sopenharmony_cinfs4_destroy_seqid_counter(struct nfs_seqid_counter *sc)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	rpc_destroy_wait_queue(&sc->wait);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci/*
49062306a36Sopenharmony_ci * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to
49162306a36Sopenharmony_ci * create a new state_owner.
49262306a36Sopenharmony_ci *
49362306a36Sopenharmony_ci */
49462306a36Sopenharmony_cistatic struct nfs4_state_owner *
49562306a36Sopenharmony_cinfs4_alloc_state_owner(struct nfs_server *server,
49662306a36Sopenharmony_ci		const struct cred *cred,
49762306a36Sopenharmony_ci		gfp_t gfp_flags)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct nfs4_state_owner *sp;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	sp = kzalloc(sizeof(*sp), gfp_flags);
50262306a36Sopenharmony_ci	if (!sp)
50362306a36Sopenharmony_ci		return NULL;
50462306a36Sopenharmony_ci	sp->so_seqid.owner_id = ida_alloc(&server->openowner_id, gfp_flags);
50562306a36Sopenharmony_ci	if (sp->so_seqid.owner_id < 0) {
50662306a36Sopenharmony_ci		kfree(sp);
50762306a36Sopenharmony_ci		return NULL;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci	sp->so_server = server;
51062306a36Sopenharmony_ci	sp->so_cred = get_cred(cred);
51162306a36Sopenharmony_ci	spin_lock_init(&sp->so_lock);
51262306a36Sopenharmony_ci	INIT_LIST_HEAD(&sp->so_states);
51362306a36Sopenharmony_ci	nfs4_init_seqid_counter(&sp->so_seqid);
51462306a36Sopenharmony_ci	atomic_set(&sp->so_count, 1);
51562306a36Sopenharmony_ci	INIT_LIST_HEAD(&sp->so_lru);
51662306a36Sopenharmony_ci	seqcount_spinlock_init(&sp->so_reclaim_seqcount, &sp->so_lock);
51762306a36Sopenharmony_ci	mutex_init(&sp->so_delegreturn_mutex);
51862306a36Sopenharmony_ci	return sp;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic void
52262306a36Sopenharmony_cinfs4_reset_state_owner(struct nfs4_state_owner *sp)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	/* This state_owner is no longer usable, but must
52562306a36Sopenharmony_ci	 * remain in place so that state recovery can find it
52662306a36Sopenharmony_ci	 * and the opens associated with it.
52762306a36Sopenharmony_ci	 * It may also be used for new 'open' request to
52862306a36Sopenharmony_ci	 * return a delegation to the server.
52962306a36Sopenharmony_ci	 * So update the 'create_time' so that it looks like
53062306a36Sopenharmony_ci	 * a new state_owner.  This will cause the server to
53162306a36Sopenharmony_ci	 * request an OPEN_CONFIRM to start a new sequence.
53262306a36Sopenharmony_ci	 */
53362306a36Sopenharmony_ci	sp->so_seqid.create_time = ktime_get();
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic void nfs4_free_state_owner(struct nfs4_state_owner *sp)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	nfs4_destroy_seqid_counter(&sp->so_seqid);
53962306a36Sopenharmony_ci	put_cred(sp->so_cred);
54062306a36Sopenharmony_ci	ida_free(&sp->so_server->openowner_id, sp->so_seqid.owner_id);
54162306a36Sopenharmony_ci	kfree(sp);
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic void nfs4_gc_state_owners(struct nfs_server *server)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
54762306a36Sopenharmony_ci	struct nfs4_state_owner *sp, *tmp;
54862306a36Sopenharmony_ci	unsigned long time_min, time_max;
54962306a36Sopenharmony_ci	LIST_HEAD(doomed);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
55262306a36Sopenharmony_ci	time_max = jiffies;
55362306a36Sopenharmony_ci	time_min = (long)time_max - (long)clp->cl_lease_time;
55462306a36Sopenharmony_ci	list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) {
55562306a36Sopenharmony_ci		/* NB: LRU is sorted so that oldest is at the head */
55662306a36Sopenharmony_ci		if (time_in_range(sp->so_expires, time_min, time_max))
55762306a36Sopenharmony_ci			break;
55862306a36Sopenharmony_ci		list_move(&sp->so_lru, &doomed);
55962306a36Sopenharmony_ci		nfs4_remove_state_owner_locked(sp);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	list_for_each_entry_safe(sp, tmp, &doomed, so_lru) {
56462306a36Sopenharmony_ci		list_del(&sp->so_lru);
56562306a36Sopenharmony_ci		nfs4_free_state_owner(sp);
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/**
57062306a36Sopenharmony_ci * nfs4_get_state_owner - Look up a state owner given a credential
57162306a36Sopenharmony_ci * @server: nfs_server to search
57262306a36Sopenharmony_ci * @cred: RPC credential to match
57362306a36Sopenharmony_ci * @gfp_flags: allocation mode
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * Returns a pointer to an instantiated nfs4_state_owner struct, or NULL.
57662306a36Sopenharmony_ci */
57762306a36Sopenharmony_cistruct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server,
57862306a36Sopenharmony_ci					      const struct cred *cred,
57962306a36Sopenharmony_ci					      gfp_t gfp_flags)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
58262306a36Sopenharmony_ci	struct nfs4_state_owner *sp, *new;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
58562306a36Sopenharmony_ci	sp = nfs4_find_state_owner_locked(server, cred);
58662306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
58762306a36Sopenharmony_ci	if (sp != NULL)
58862306a36Sopenharmony_ci		goto out;
58962306a36Sopenharmony_ci	new = nfs4_alloc_state_owner(server, cred, gfp_flags);
59062306a36Sopenharmony_ci	if (new == NULL)
59162306a36Sopenharmony_ci		goto out;
59262306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
59362306a36Sopenharmony_ci	sp = nfs4_insert_state_owner_locked(new);
59462306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
59562306a36Sopenharmony_ci	if (sp != new)
59662306a36Sopenharmony_ci		nfs4_free_state_owner(new);
59762306a36Sopenharmony_ciout:
59862306a36Sopenharmony_ci	nfs4_gc_state_owners(server);
59962306a36Sopenharmony_ci	return sp;
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci/**
60362306a36Sopenharmony_ci * nfs4_put_state_owner - Release a nfs4_state_owner
60462306a36Sopenharmony_ci * @sp: state owner data to release
60562306a36Sopenharmony_ci *
60662306a36Sopenharmony_ci * Note that we keep released state owners on an LRU
60762306a36Sopenharmony_ci * list.
60862306a36Sopenharmony_ci * This caches valid state owners so that they can be
60962306a36Sopenharmony_ci * reused, to avoid the OPEN_CONFIRM on minor version 0.
61062306a36Sopenharmony_ci * It also pins the uniquifier of dropped state owners for
61162306a36Sopenharmony_ci * a while, to ensure that those state owner names are
61262306a36Sopenharmony_ci * never reused.
61362306a36Sopenharmony_ci */
61462306a36Sopenharmony_civoid nfs4_put_state_owner(struct nfs4_state_owner *sp)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	struct nfs_server *server = sp->so_server;
61762306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock))
62062306a36Sopenharmony_ci		return;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	sp->so_expires = jiffies;
62362306a36Sopenharmony_ci	list_add_tail(&sp->so_lru, &server->state_owners_lru);
62462306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/**
62862306a36Sopenharmony_ci * nfs4_purge_state_owners - Release all cached state owners
62962306a36Sopenharmony_ci * @server: nfs_server with cached state owners to release
63062306a36Sopenharmony_ci * @head: resulting list of state owners
63162306a36Sopenharmony_ci *
63262306a36Sopenharmony_ci * Called at umount time.  Remaining state owners will be on
63362306a36Sopenharmony_ci * the LRU with ref count of zero.
63462306a36Sopenharmony_ci * Note that the state owners are not freed, but are added
63562306a36Sopenharmony_ci * to the list @head, which can later be used as an argument
63662306a36Sopenharmony_ci * to nfs4_free_state_owners.
63762306a36Sopenharmony_ci */
63862306a36Sopenharmony_civoid nfs4_purge_state_owners(struct nfs_server *server, struct list_head *head)
63962306a36Sopenharmony_ci{
64062306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
64162306a36Sopenharmony_ci	struct nfs4_state_owner *sp, *tmp;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
64462306a36Sopenharmony_ci	list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) {
64562306a36Sopenharmony_ci		list_move(&sp->so_lru, head);
64662306a36Sopenharmony_ci		nfs4_remove_state_owner_locked(sp);
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci/**
65262306a36Sopenharmony_ci * nfs4_free_state_owners - Release all cached state owners
65362306a36Sopenharmony_ci * @head: resulting list of state owners
65462306a36Sopenharmony_ci *
65562306a36Sopenharmony_ci * Frees a list of state owners that was generated by
65662306a36Sopenharmony_ci * nfs4_purge_state_owners
65762306a36Sopenharmony_ci */
65862306a36Sopenharmony_civoid nfs4_free_state_owners(struct list_head *head)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	struct nfs4_state_owner *sp, *tmp;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	list_for_each_entry_safe(sp, tmp, head, so_lru) {
66362306a36Sopenharmony_ci		list_del(&sp->so_lru);
66462306a36Sopenharmony_ci		nfs4_free_state_owner(sp);
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic struct nfs4_state *
66962306a36Sopenharmony_cinfs4_alloc_open_state(void)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct nfs4_state *state;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	state = kzalloc(sizeof(*state), GFP_KERNEL_ACCOUNT);
67462306a36Sopenharmony_ci	if (!state)
67562306a36Sopenharmony_ci		return NULL;
67662306a36Sopenharmony_ci	refcount_set(&state->count, 1);
67762306a36Sopenharmony_ci	INIT_LIST_HEAD(&state->lock_states);
67862306a36Sopenharmony_ci	spin_lock_init(&state->state_lock);
67962306a36Sopenharmony_ci	seqlock_init(&state->seqlock);
68062306a36Sopenharmony_ci	init_waitqueue_head(&state->waitq);
68162306a36Sopenharmony_ci	return state;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_civoid
68562306a36Sopenharmony_cinfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	if (state->state == fmode)
68862306a36Sopenharmony_ci		return;
68962306a36Sopenharmony_ci	/* NB! List reordering - see the reclaim code for why.  */
69062306a36Sopenharmony_ci	if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
69162306a36Sopenharmony_ci		if (fmode & FMODE_WRITE)
69262306a36Sopenharmony_ci			list_move(&state->open_states, &state->owner->so_states);
69362306a36Sopenharmony_ci		else
69462306a36Sopenharmony_ci			list_move_tail(&state->open_states, &state->owner->so_states);
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci	state->state = fmode;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic struct nfs4_state *
70062306a36Sopenharmony_ci__nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
70362306a36Sopenharmony_ci	struct nfs4_state *state;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	list_for_each_entry_rcu(state, &nfsi->open_states, inode_states) {
70662306a36Sopenharmony_ci		if (state->owner != owner)
70762306a36Sopenharmony_ci			continue;
70862306a36Sopenharmony_ci		if (!nfs4_valid_open_stateid(state))
70962306a36Sopenharmony_ci			continue;
71062306a36Sopenharmony_ci		if (refcount_inc_not_zero(&state->count))
71162306a36Sopenharmony_ci			return state;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci	return NULL;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic void
71762306a36Sopenharmony_cinfs4_free_open_state(struct nfs4_state *state)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	kfree_rcu(state, rcu_head);
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistruct nfs4_state *
72362306a36Sopenharmony_cinfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	struct nfs4_state *state, *new;
72662306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	rcu_read_lock();
72962306a36Sopenharmony_ci	state = __nfs4_find_state_byowner(inode, owner);
73062306a36Sopenharmony_ci	rcu_read_unlock();
73162306a36Sopenharmony_ci	if (state)
73262306a36Sopenharmony_ci		goto out;
73362306a36Sopenharmony_ci	new = nfs4_alloc_open_state();
73462306a36Sopenharmony_ci	spin_lock(&owner->so_lock);
73562306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
73662306a36Sopenharmony_ci	state = __nfs4_find_state_byowner(inode, owner);
73762306a36Sopenharmony_ci	if (state == NULL && new != NULL) {
73862306a36Sopenharmony_ci		state = new;
73962306a36Sopenharmony_ci		state->owner = owner;
74062306a36Sopenharmony_ci		atomic_inc(&owner->so_count);
74162306a36Sopenharmony_ci		ihold(inode);
74262306a36Sopenharmony_ci		state->inode = inode;
74362306a36Sopenharmony_ci		list_add_rcu(&state->inode_states, &nfsi->open_states);
74462306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
74562306a36Sopenharmony_ci		/* Note: The reclaim code dictates that we add stateless
74662306a36Sopenharmony_ci		 * and read-only stateids to the end of the list */
74762306a36Sopenharmony_ci		list_add_tail(&state->open_states, &owner->so_states);
74862306a36Sopenharmony_ci		spin_unlock(&owner->so_lock);
74962306a36Sopenharmony_ci	} else {
75062306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
75162306a36Sopenharmony_ci		spin_unlock(&owner->so_lock);
75262306a36Sopenharmony_ci		if (new)
75362306a36Sopenharmony_ci			nfs4_free_open_state(new);
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ciout:
75662306a36Sopenharmony_ci	return state;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_civoid nfs4_put_open_state(struct nfs4_state *state)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	struct inode *inode = state->inode;
76262306a36Sopenharmony_ci	struct nfs4_state_owner *owner = state->owner;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	if (!refcount_dec_and_lock(&state->count, &owner->so_lock))
76562306a36Sopenharmony_ci		return;
76662306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
76762306a36Sopenharmony_ci	list_del_rcu(&state->inode_states);
76862306a36Sopenharmony_ci	list_del(&state->open_states);
76962306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
77062306a36Sopenharmony_ci	spin_unlock(&owner->so_lock);
77162306a36Sopenharmony_ci	nfs4_inode_return_delegation_on_close(inode);
77262306a36Sopenharmony_ci	iput(inode);
77362306a36Sopenharmony_ci	nfs4_free_open_state(state);
77462306a36Sopenharmony_ci	nfs4_put_state_owner(owner);
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci/*
77862306a36Sopenharmony_ci * Close the current file.
77962306a36Sopenharmony_ci */
78062306a36Sopenharmony_cistatic void __nfs4_close(struct nfs4_state *state,
78162306a36Sopenharmony_ci		fmode_t fmode, gfp_t gfp_mask, int wait)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct nfs4_state_owner *owner = state->owner;
78462306a36Sopenharmony_ci	int call_close = 0;
78562306a36Sopenharmony_ci	fmode_t newstate;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	atomic_inc(&owner->so_count);
78862306a36Sopenharmony_ci	/* Protect against nfs4_find_state() */
78962306a36Sopenharmony_ci	spin_lock(&owner->so_lock);
79062306a36Sopenharmony_ci	switch (fmode & (FMODE_READ | FMODE_WRITE)) {
79162306a36Sopenharmony_ci		case FMODE_READ:
79262306a36Sopenharmony_ci			state->n_rdonly--;
79362306a36Sopenharmony_ci			break;
79462306a36Sopenharmony_ci		case FMODE_WRITE:
79562306a36Sopenharmony_ci			state->n_wronly--;
79662306a36Sopenharmony_ci			break;
79762306a36Sopenharmony_ci		case FMODE_READ|FMODE_WRITE:
79862306a36Sopenharmony_ci			state->n_rdwr--;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci	newstate = FMODE_READ|FMODE_WRITE;
80162306a36Sopenharmony_ci	if (state->n_rdwr == 0) {
80262306a36Sopenharmony_ci		if (state->n_rdonly == 0) {
80362306a36Sopenharmony_ci			newstate &= ~FMODE_READ;
80462306a36Sopenharmony_ci			call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags);
80562306a36Sopenharmony_ci			call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
80662306a36Sopenharmony_ci		}
80762306a36Sopenharmony_ci		if (state->n_wronly == 0) {
80862306a36Sopenharmony_ci			newstate &= ~FMODE_WRITE;
80962306a36Sopenharmony_ci			call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags);
81062306a36Sopenharmony_ci			call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
81162306a36Sopenharmony_ci		}
81262306a36Sopenharmony_ci		if (newstate == 0)
81362306a36Sopenharmony_ci			clear_bit(NFS_DELEGATED_STATE, &state->flags);
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci	nfs4_state_set_mode_locked(state, newstate);
81662306a36Sopenharmony_ci	spin_unlock(&owner->so_lock);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (!call_close) {
81962306a36Sopenharmony_ci		nfs4_put_open_state(state);
82062306a36Sopenharmony_ci		nfs4_put_state_owner(owner);
82162306a36Sopenharmony_ci	} else
82262306a36Sopenharmony_ci		nfs4_do_close(state, gfp_mask, wait);
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_civoid nfs4_close_state(struct nfs4_state *state, fmode_t fmode)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	__nfs4_close(state, fmode, GFP_KERNEL, 0);
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_civoid nfs4_close_sync(struct nfs4_state *state, fmode_t fmode)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	__nfs4_close(state, fmode, GFP_KERNEL, 1);
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci/*
83662306a36Sopenharmony_ci * Search the state->lock_states for an existing lock_owner
83762306a36Sopenharmony_ci * that is compatible with either of the given owners.
83862306a36Sopenharmony_ci * If the second is non-zero, then the first refers to a Posix-lock
83962306a36Sopenharmony_ci * owner (current->files) and the second refers to a flock/OFD
84062306a36Sopenharmony_ci * owner (struct file*).  In that case, prefer a match for the first
84162306a36Sopenharmony_ci * owner.
84262306a36Sopenharmony_ci * If both sorts of locks are held on the one file we cannot know
84362306a36Sopenharmony_ci * which stateid was intended to be used, so a "correct" choice cannot
84462306a36Sopenharmony_ci * be made.  Failing that, a "consistent" choice is preferable.  The
84562306a36Sopenharmony_ci * consistent choice we make is to prefer the first owner, that of a
84662306a36Sopenharmony_ci * Posix lock.
84762306a36Sopenharmony_ci */
84862306a36Sopenharmony_cistatic struct nfs4_lock_state *
84962306a36Sopenharmony_ci__nfs4_find_lock_state(struct nfs4_state *state,
85062306a36Sopenharmony_ci		       fl_owner_t fl_owner, fl_owner_t fl_owner2)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	struct nfs4_lock_state *pos, *ret = NULL;
85362306a36Sopenharmony_ci	list_for_each_entry(pos, &state->lock_states, ls_locks) {
85462306a36Sopenharmony_ci		if (pos->ls_owner == fl_owner) {
85562306a36Sopenharmony_ci			ret = pos;
85662306a36Sopenharmony_ci			break;
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci		if (pos->ls_owner == fl_owner2)
85962306a36Sopenharmony_ci			ret = pos;
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci	if (ret)
86262306a36Sopenharmony_ci		refcount_inc(&ret->ls_count);
86362306a36Sopenharmony_ci	return ret;
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci/*
86762306a36Sopenharmony_ci * Return a compatible lock_state. If no initialized lock_state structure
86862306a36Sopenharmony_ci * exists, return an uninitialized one.
86962306a36Sopenharmony_ci *
87062306a36Sopenharmony_ci */
87162306a36Sopenharmony_cistatic struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct nfs4_lock_state *lsp;
87462306a36Sopenharmony_ci	struct nfs_server *server = state->owner->so_server;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	lsp = kzalloc(sizeof(*lsp), GFP_KERNEL_ACCOUNT);
87762306a36Sopenharmony_ci	if (lsp == NULL)
87862306a36Sopenharmony_ci		return NULL;
87962306a36Sopenharmony_ci	nfs4_init_seqid_counter(&lsp->ls_seqid);
88062306a36Sopenharmony_ci	refcount_set(&lsp->ls_count, 1);
88162306a36Sopenharmony_ci	lsp->ls_state = state;
88262306a36Sopenharmony_ci	lsp->ls_owner = fl_owner;
88362306a36Sopenharmony_ci	lsp->ls_seqid.owner_id = ida_alloc(&server->lockowner_id, GFP_KERNEL_ACCOUNT);
88462306a36Sopenharmony_ci	if (lsp->ls_seqid.owner_id < 0)
88562306a36Sopenharmony_ci		goto out_free;
88662306a36Sopenharmony_ci	INIT_LIST_HEAD(&lsp->ls_locks);
88762306a36Sopenharmony_ci	return lsp;
88862306a36Sopenharmony_ciout_free:
88962306a36Sopenharmony_ci	kfree(lsp);
89062306a36Sopenharmony_ci	return NULL;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_civoid nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	ida_free(&server->lockowner_id, lsp->ls_seqid.owner_id);
89662306a36Sopenharmony_ci	nfs4_destroy_seqid_counter(&lsp->ls_seqid);
89762306a36Sopenharmony_ci	kfree(lsp);
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci/*
90162306a36Sopenharmony_ci * Return a compatible lock_state. If no initialized lock_state structure
90262306a36Sopenharmony_ci * exists, return an uninitialized one.
90362306a36Sopenharmony_ci *
90462306a36Sopenharmony_ci */
90562306a36Sopenharmony_cistatic struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct nfs4_lock_state *lsp, *new = NULL;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	for(;;) {
91062306a36Sopenharmony_ci		spin_lock(&state->state_lock);
91162306a36Sopenharmony_ci		lsp = __nfs4_find_lock_state(state, owner, NULL);
91262306a36Sopenharmony_ci		if (lsp != NULL)
91362306a36Sopenharmony_ci			break;
91462306a36Sopenharmony_ci		if (new != NULL) {
91562306a36Sopenharmony_ci			list_add(&new->ls_locks, &state->lock_states);
91662306a36Sopenharmony_ci			set_bit(LK_STATE_IN_USE, &state->flags);
91762306a36Sopenharmony_ci			lsp = new;
91862306a36Sopenharmony_ci			new = NULL;
91962306a36Sopenharmony_ci			break;
92062306a36Sopenharmony_ci		}
92162306a36Sopenharmony_ci		spin_unlock(&state->state_lock);
92262306a36Sopenharmony_ci		new = nfs4_alloc_lock_state(state, owner);
92362306a36Sopenharmony_ci		if (new == NULL)
92462306a36Sopenharmony_ci			return NULL;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci	spin_unlock(&state->state_lock);
92762306a36Sopenharmony_ci	if (new != NULL)
92862306a36Sopenharmony_ci		nfs4_free_lock_state(state->owner->so_server, new);
92962306a36Sopenharmony_ci	return lsp;
93062306a36Sopenharmony_ci}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci/*
93362306a36Sopenharmony_ci * Release reference to lock_state, and free it if we see that
93462306a36Sopenharmony_ci * it is no longer in use
93562306a36Sopenharmony_ci */
93662306a36Sopenharmony_civoid nfs4_put_lock_state(struct nfs4_lock_state *lsp)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	struct nfs_server *server;
93962306a36Sopenharmony_ci	struct nfs4_state *state;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (lsp == NULL)
94262306a36Sopenharmony_ci		return;
94362306a36Sopenharmony_ci	state = lsp->ls_state;
94462306a36Sopenharmony_ci	if (!refcount_dec_and_lock(&lsp->ls_count, &state->state_lock))
94562306a36Sopenharmony_ci		return;
94662306a36Sopenharmony_ci	list_del(&lsp->ls_locks);
94762306a36Sopenharmony_ci	if (list_empty(&state->lock_states))
94862306a36Sopenharmony_ci		clear_bit(LK_STATE_IN_USE, &state->flags);
94962306a36Sopenharmony_ci	spin_unlock(&state->state_lock);
95062306a36Sopenharmony_ci	server = state->owner->so_server;
95162306a36Sopenharmony_ci	if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
95262306a36Sopenharmony_ci		struct nfs_client *clp = server->nfs_client;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		clp->cl_mvops->free_lock_state(server, lsp);
95562306a36Sopenharmony_ci	} else
95662306a36Sopenharmony_ci		nfs4_free_lock_state(server, lsp);
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	struct nfs4_lock_state *lsp = src->fl_u.nfs4_fl.owner;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	dst->fl_u.nfs4_fl.owner = lsp;
96462306a36Sopenharmony_ci	refcount_inc(&lsp->ls_count);
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_cistatic void nfs4_fl_release_lock(struct file_lock *fl)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	nfs4_put_lock_state(fl->fl_u.nfs4_fl.owner);
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic const struct file_lock_operations nfs4_fl_lock_ops = {
97362306a36Sopenharmony_ci	.fl_copy_lock = nfs4_fl_copy_lock,
97462306a36Sopenharmony_ci	.fl_release_private = nfs4_fl_release_lock,
97562306a36Sopenharmony_ci};
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ciint nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct nfs4_lock_state *lsp;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	if (fl->fl_ops != NULL)
98262306a36Sopenharmony_ci		return 0;
98362306a36Sopenharmony_ci	lsp = nfs4_get_lock_state(state, fl->fl_owner);
98462306a36Sopenharmony_ci	if (lsp == NULL)
98562306a36Sopenharmony_ci		return -ENOMEM;
98662306a36Sopenharmony_ci	fl->fl_u.nfs4_fl.owner = lsp;
98762306a36Sopenharmony_ci	fl->fl_ops = &nfs4_fl_lock_ops;
98862306a36Sopenharmony_ci	return 0;
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic int nfs4_copy_lock_stateid(nfs4_stateid *dst,
99262306a36Sopenharmony_ci		struct nfs4_state *state,
99362306a36Sopenharmony_ci		const struct nfs_lock_context *l_ctx)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct nfs4_lock_state *lsp;
99662306a36Sopenharmony_ci	fl_owner_t fl_owner, fl_flock_owner;
99762306a36Sopenharmony_ci	int ret = -ENOENT;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (l_ctx == NULL)
100062306a36Sopenharmony_ci		goto out;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
100362306a36Sopenharmony_ci		goto out;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	fl_owner = l_ctx->lockowner;
100662306a36Sopenharmony_ci	fl_flock_owner = l_ctx->open_context->flock_owner;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	spin_lock(&state->state_lock);
100962306a36Sopenharmony_ci	lsp = __nfs4_find_lock_state(state, fl_owner, fl_flock_owner);
101062306a36Sopenharmony_ci	if (lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags))
101162306a36Sopenharmony_ci		ret = -EIO;
101262306a36Sopenharmony_ci	else if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) {
101362306a36Sopenharmony_ci		nfs4_stateid_copy(dst, &lsp->ls_stateid);
101462306a36Sopenharmony_ci		ret = 0;
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci	spin_unlock(&state->state_lock);
101762306a36Sopenharmony_ci	nfs4_put_lock_state(lsp);
101862306a36Sopenharmony_ciout:
101962306a36Sopenharmony_ci	return ret;
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_cibool nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	bool ret;
102562306a36Sopenharmony_ci	const nfs4_stateid *src;
102662306a36Sopenharmony_ci	int seq;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	do {
102962306a36Sopenharmony_ci		ret = false;
103062306a36Sopenharmony_ci		src = &zero_stateid;
103162306a36Sopenharmony_ci		seq = read_seqbegin(&state->seqlock);
103262306a36Sopenharmony_ci		if (test_bit(NFS_OPEN_STATE, &state->flags)) {
103362306a36Sopenharmony_ci			src = &state->open_stateid;
103462306a36Sopenharmony_ci			ret = true;
103562306a36Sopenharmony_ci		}
103662306a36Sopenharmony_ci		nfs4_stateid_copy(dst, src);
103762306a36Sopenharmony_ci	} while (read_seqretry(&state->seqlock, seq));
103862306a36Sopenharmony_ci	return ret;
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci/*
104262306a36Sopenharmony_ci * Byte-range lock aware utility to initialize the stateid of read/write
104362306a36Sopenharmony_ci * requests.
104462306a36Sopenharmony_ci */
104562306a36Sopenharmony_ciint nfs4_select_rw_stateid(struct nfs4_state *state,
104662306a36Sopenharmony_ci		fmode_t fmode, const struct nfs_lock_context *l_ctx,
104762306a36Sopenharmony_ci		nfs4_stateid *dst, const struct cred **cred)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	int ret;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	if (!nfs4_valid_open_stateid(state))
105262306a36Sopenharmony_ci		return -EIO;
105362306a36Sopenharmony_ci	if (cred != NULL)
105462306a36Sopenharmony_ci		*cred = NULL;
105562306a36Sopenharmony_ci	ret = nfs4_copy_lock_stateid(dst, state, l_ctx);
105662306a36Sopenharmony_ci	if (ret == -EIO)
105762306a36Sopenharmony_ci		/* A lost lock - don't even consider delegations */
105862306a36Sopenharmony_ci		goto out;
105962306a36Sopenharmony_ci	/* returns true if delegation stateid found and copied */
106062306a36Sopenharmony_ci	if (nfs4_copy_delegation_stateid(state->inode, fmode, dst, cred)) {
106162306a36Sopenharmony_ci		ret = 0;
106262306a36Sopenharmony_ci		goto out;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci	if (ret != -ENOENT)
106562306a36Sopenharmony_ci		/* nfs4_copy_delegation_stateid() didn't over-write
106662306a36Sopenharmony_ci		 * dst, so it still has the lock stateid which we now
106762306a36Sopenharmony_ci		 * choose to use.
106862306a36Sopenharmony_ci		 */
106962306a36Sopenharmony_ci		goto out;
107062306a36Sopenharmony_ci	ret = nfs4_copy_open_stateid(dst, state) ? 0 : -EAGAIN;
107162306a36Sopenharmony_ciout:
107262306a36Sopenharmony_ci	if (nfs_server_capable(state->inode, NFS_CAP_STATEID_NFSV41))
107362306a36Sopenharmony_ci		dst->seqid = 0;
107462306a36Sopenharmony_ci	return ret;
107562306a36Sopenharmony_ci}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_cistruct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	struct nfs_seqid *new;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	new = kmalloc(sizeof(*new), gfp_mask);
108262306a36Sopenharmony_ci	if (new == NULL)
108362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
108462306a36Sopenharmony_ci	new->sequence = counter;
108562306a36Sopenharmony_ci	INIT_LIST_HEAD(&new->list);
108662306a36Sopenharmony_ci	new->task = NULL;
108762306a36Sopenharmony_ci	return new;
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_civoid nfs_release_seqid(struct nfs_seqid *seqid)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	struct nfs_seqid_counter *sequence;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	if (seqid == NULL || list_empty(&seqid->list))
109562306a36Sopenharmony_ci		return;
109662306a36Sopenharmony_ci	sequence = seqid->sequence;
109762306a36Sopenharmony_ci	spin_lock(&sequence->lock);
109862306a36Sopenharmony_ci	list_del_init(&seqid->list);
109962306a36Sopenharmony_ci	if (!list_empty(&sequence->list)) {
110062306a36Sopenharmony_ci		struct nfs_seqid *next;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci		next = list_first_entry(&sequence->list,
110362306a36Sopenharmony_ci				struct nfs_seqid, list);
110462306a36Sopenharmony_ci		rpc_wake_up_queued_task(&sequence->wait, next->task);
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci	spin_unlock(&sequence->lock);
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_civoid nfs_free_seqid(struct nfs_seqid *seqid)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	nfs_release_seqid(seqid);
111262306a36Sopenharmony_ci	kfree(seqid);
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci/*
111662306a36Sopenharmony_ci * Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
111762306a36Sopenharmony_ci * failed with a seqid incrementing error -
111862306a36Sopenharmony_ci * see comments nfs4.h:seqid_mutating_error()
111962306a36Sopenharmony_ci */
112062306a36Sopenharmony_cistatic void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	switch (status) {
112362306a36Sopenharmony_ci		case 0:
112462306a36Sopenharmony_ci			break;
112562306a36Sopenharmony_ci		case -NFS4ERR_BAD_SEQID:
112662306a36Sopenharmony_ci			if (seqid->sequence->flags & NFS_SEQID_CONFIRMED)
112762306a36Sopenharmony_ci				return;
112862306a36Sopenharmony_ci			pr_warn_ratelimited("NFS: v4 server returned a bad"
112962306a36Sopenharmony_ci					" sequence-id error on an"
113062306a36Sopenharmony_ci					" unconfirmed sequence %p!\n",
113162306a36Sopenharmony_ci					seqid->sequence);
113262306a36Sopenharmony_ci			return;
113362306a36Sopenharmony_ci		case -NFS4ERR_STALE_CLIENTID:
113462306a36Sopenharmony_ci		case -NFS4ERR_STALE_STATEID:
113562306a36Sopenharmony_ci		case -NFS4ERR_BAD_STATEID:
113662306a36Sopenharmony_ci		case -NFS4ERR_BADXDR:
113762306a36Sopenharmony_ci		case -NFS4ERR_RESOURCE:
113862306a36Sopenharmony_ci		case -NFS4ERR_NOFILEHANDLE:
113962306a36Sopenharmony_ci		case -NFS4ERR_MOVED:
114062306a36Sopenharmony_ci			/* Non-seqid mutating errors */
114162306a36Sopenharmony_ci			return;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci	/*
114462306a36Sopenharmony_ci	 * Note: no locking needed as we are guaranteed to be first
114562306a36Sopenharmony_ci	 * on the sequence list
114662306a36Sopenharmony_ci	 */
114762306a36Sopenharmony_ci	seqid->sequence->counter++;
114862306a36Sopenharmony_ci}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_civoid nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
115162306a36Sopenharmony_ci{
115262306a36Sopenharmony_ci	struct nfs4_state_owner *sp;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	if (seqid == NULL)
115562306a36Sopenharmony_ci		return;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	sp = container_of(seqid->sequence, struct nfs4_state_owner, so_seqid);
115862306a36Sopenharmony_ci	if (status == -NFS4ERR_BAD_SEQID)
115962306a36Sopenharmony_ci		nfs4_reset_state_owner(sp);
116062306a36Sopenharmony_ci	if (!nfs4_has_session(sp->so_server->nfs_client))
116162306a36Sopenharmony_ci		nfs_increment_seqid(status, seqid);
116262306a36Sopenharmony_ci}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci/*
116562306a36Sopenharmony_ci * Increment the seqid if the LOCK/LOCKU succeeded, or
116662306a36Sopenharmony_ci * failed with a seqid incrementing error -
116762306a36Sopenharmony_ci * see comments nfs4.h:seqid_mutating_error()
116862306a36Sopenharmony_ci */
116962306a36Sopenharmony_civoid nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid)
117062306a36Sopenharmony_ci{
117162306a36Sopenharmony_ci	if (seqid != NULL)
117262306a36Sopenharmony_ci		nfs_increment_seqid(status, seqid);
117362306a36Sopenharmony_ci}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ciint nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task)
117662306a36Sopenharmony_ci{
117762306a36Sopenharmony_ci	struct nfs_seqid_counter *sequence;
117862306a36Sopenharmony_ci	int status = 0;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	if (seqid == NULL)
118162306a36Sopenharmony_ci		goto out;
118262306a36Sopenharmony_ci	sequence = seqid->sequence;
118362306a36Sopenharmony_ci	spin_lock(&sequence->lock);
118462306a36Sopenharmony_ci	seqid->task = task;
118562306a36Sopenharmony_ci	if (list_empty(&seqid->list))
118662306a36Sopenharmony_ci		list_add_tail(&seqid->list, &sequence->list);
118762306a36Sopenharmony_ci	if (list_first_entry(&sequence->list, struct nfs_seqid, list) == seqid)
118862306a36Sopenharmony_ci		goto unlock;
118962306a36Sopenharmony_ci	rpc_sleep_on(&sequence->wait, task, NULL);
119062306a36Sopenharmony_ci	status = -EAGAIN;
119162306a36Sopenharmony_ciunlock:
119262306a36Sopenharmony_ci	spin_unlock(&sequence->lock);
119362306a36Sopenharmony_ciout:
119462306a36Sopenharmony_ci	return status;
119562306a36Sopenharmony_ci}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_cistatic int nfs4_run_state_manager(void *);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic void nfs4_clear_state_manager_bit(struct nfs_client *clp)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	clear_and_wake_up_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
120262306a36Sopenharmony_ci	rpc_wake_up(&clp->cl_rpcwaitq);
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci/*
120662306a36Sopenharmony_ci * Schedule the nfs_client asynchronous state management routine
120762306a36Sopenharmony_ci */
120862306a36Sopenharmony_civoid nfs4_schedule_state_manager(struct nfs_client *clp)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	struct task_struct *task;
121162306a36Sopenharmony_ci	char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
121262306a36Sopenharmony_ci	struct rpc_clnt *clnt = clp->cl_rpcclient;
121362306a36Sopenharmony_ci	bool swapon = false;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	if (clnt->cl_shutdown)
121662306a36Sopenharmony_ci		return;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (atomic_read(&clnt->cl_swapper)) {
122162306a36Sopenharmony_ci		swapon = !test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE,
122262306a36Sopenharmony_ci					   &clp->cl_state);
122362306a36Sopenharmony_ci		if (!swapon) {
122462306a36Sopenharmony_ci			wake_up_var(&clp->cl_state);
122562306a36Sopenharmony_ci			return;
122662306a36Sopenharmony_ci		}
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
123062306a36Sopenharmony_ci		return;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	__module_get(THIS_MODULE);
123362306a36Sopenharmony_ci	refcount_inc(&clp->cl_count);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	/* The rcu_read_lock() is not strictly necessary, as the state
123662306a36Sopenharmony_ci	 * manager is the only thread that ever changes the rpc_xprt
123762306a36Sopenharmony_ci	 * after it's initialized.  At this point, we're single threaded. */
123862306a36Sopenharmony_ci	rcu_read_lock();
123962306a36Sopenharmony_ci	snprintf(buf, sizeof(buf), "%s-manager",
124062306a36Sopenharmony_ci			rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
124162306a36Sopenharmony_ci	rcu_read_unlock();
124262306a36Sopenharmony_ci	task = kthread_run(nfs4_run_state_manager, clp, "%s", buf);
124362306a36Sopenharmony_ci	if (IS_ERR(task)) {
124462306a36Sopenharmony_ci		printk(KERN_ERR "%s: kthread_run: %ld\n",
124562306a36Sopenharmony_ci			__func__, PTR_ERR(task));
124662306a36Sopenharmony_ci		if (!nfs_client_init_is_complete(clp))
124762306a36Sopenharmony_ci			nfs_mark_client_ready(clp, PTR_ERR(task));
124862306a36Sopenharmony_ci		if (swapon)
124962306a36Sopenharmony_ci			clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
125062306a36Sopenharmony_ci		nfs4_clear_state_manager_bit(clp);
125162306a36Sopenharmony_ci		nfs_put_client(clp);
125262306a36Sopenharmony_ci		module_put(THIS_MODULE);
125362306a36Sopenharmony_ci	}
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci/*
125762306a36Sopenharmony_ci * Schedule a lease recovery attempt
125862306a36Sopenharmony_ci */
125962306a36Sopenharmony_civoid nfs4_schedule_lease_recovery(struct nfs_client *clp)
126062306a36Sopenharmony_ci{
126162306a36Sopenharmony_ci	if (!clp)
126262306a36Sopenharmony_ci		return;
126362306a36Sopenharmony_ci	if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
126462306a36Sopenharmony_ci		set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
126562306a36Sopenharmony_ci	dprintk("%s: scheduling lease recovery for server %s\n", __func__,
126662306a36Sopenharmony_ci			clp->cl_hostname);
126762306a36Sopenharmony_ci	nfs4_schedule_state_manager(clp);
126862306a36Sopenharmony_ci}
126962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_schedule_lease_recovery);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci/**
127262306a36Sopenharmony_ci * nfs4_schedule_migration_recovery - trigger migration recovery
127362306a36Sopenharmony_ci *
127462306a36Sopenharmony_ci * @server: FSID that is migrating
127562306a36Sopenharmony_ci *
127662306a36Sopenharmony_ci * Returns zero if recovery has started, otherwise a negative NFS4ERR
127762306a36Sopenharmony_ci * value is returned.
127862306a36Sopenharmony_ci */
127962306a36Sopenharmony_ciint nfs4_schedule_migration_recovery(const struct nfs_server *server)
128062306a36Sopenharmony_ci{
128162306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	if (server->fh_expire_type != NFS4_FH_PERSISTENT) {
128462306a36Sopenharmony_ci		pr_err("NFS: volatile file handles not supported (server %s)\n",
128562306a36Sopenharmony_ci				clp->cl_hostname);
128662306a36Sopenharmony_ci		return -NFS4ERR_IO;
128762306a36Sopenharmony_ci	}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	if (test_bit(NFS_MIG_FAILED, &server->mig_status))
129062306a36Sopenharmony_ci		return -NFS4ERR_IO;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	dprintk("%s: scheduling migration recovery for (%llx:%llx) on %s\n",
129362306a36Sopenharmony_ci			__func__,
129462306a36Sopenharmony_ci			(unsigned long long)server->fsid.major,
129562306a36Sopenharmony_ci			(unsigned long long)server->fsid.minor,
129662306a36Sopenharmony_ci			clp->cl_hostname);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	set_bit(NFS_MIG_IN_TRANSITION,
129962306a36Sopenharmony_ci			&((struct nfs_server *)server)->mig_status);
130062306a36Sopenharmony_ci	set_bit(NFS4CLNT_MOVED, &clp->cl_state);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	nfs4_schedule_state_manager(clp);
130362306a36Sopenharmony_ci	return 0;
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_schedule_migration_recovery);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci/**
130862306a36Sopenharmony_ci * nfs4_schedule_lease_moved_recovery - start lease-moved recovery
130962306a36Sopenharmony_ci *
131062306a36Sopenharmony_ci * @clp: server to check for moved leases
131162306a36Sopenharmony_ci *
131262306a36Sopenharmony_ci */
131362306a36Sopenharmony_civoid nfs4_schedule_lease_moved_recovery(struct nfs_client *clp)
131462306a36Sopenharmony_ci{
131562306a36Sopenharmony_ci	dprintk("%s: scheduling lease-moved recovery for client ID %llx on %s\n",
131662306a36Sopenharmony_ci		__func__, clp->cl_clientid, clp->cl_hostname);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	set_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state);
131962306a36Sopenharmony_ci	nfs4_schedule_state_manager(clp);
132062306a36Sopenharmony_ci}
132162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_schedule_lease_moved_recovery);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ciint nfs4_wait_clnt_recover(struct nfs_client *clp)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	int res;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	might_sleep();
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	refcount_inc(&clp->cl_count);
133062306a36Sopenharmony_ci	res = wait_on_bit_action(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
133162306a36Sopenharmony_ci				 nfs_wait_bit_killable,
133262306a36Sopenharmony_ci				 TASK_KILLABLE|TASK_FREEZABLE_UNSAFE);
133362306a36Sopenharmony_ci	if (res)
133462306a36Sopenharmony_ci		goto out;
133562306a36Sopenharmony_ci	if (clp->cl_cons_state < 0)
133662306a36Sopenharmony_ci		res = clp->cl_cons_state;
133762306a36Sopenharmony_ciout:
133862306a36Sopenharmony_ci	nfs_put_client(clp);
133962306a36Sopenharmony_ci	return res;
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ciint nfs4_client_recover_expired_lease(struct nfs_client *clp)
134362306a36Sopenharmony_ci{
134462306a36Sopenharmony_ci	unsigned int loop;
134562306a36Sopenharmony_ci	int ret;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
134862306a36Sopenharmony_ci		ret = nfs4_wait_clnt_recover(clp);
134962306a36Sopenharmony_ci		if (ret != 0)
135062306a36Sopenharmony_ci			break;
135162306a36Sopenharmony_ci		if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
135262306a36Sopenharmony_ci		    !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
135362306a36Sopenharmony_ci			break;
135462306a36Sopenharmony_ci		nfs4_schedule_state_manager(clp);
135562306a36Sopenharmony_ci		ret = -EIO;
135662306a36Sopenharmony_ci	}
135762306a36Sopenharmony_ci	return ret;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci/*
136162306a36Sopenharmony_ci * nfs40_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN
136262306a36Sopenharmony_ci * @clp: client to process
136362306a36Sopenharmony_ci *
136462306a36Sopenharmony_ci * Set the NFS4CLNT_LEASE_EXPIRED state in order to force a
136562306a36Sopenharmony_ci * resend of the SETCLIENTID and hence re-establish the
136662306a36Sopenharmony_ci * callback channel. Then return all existing delegations.
136762306a36Sopenharmony_ci */
136862306a36Sopenharmony_cistatic void nfs40_handle_cb_pathdown(struct nfs_client *clp)
136962306a36Sopenharmony_ci{
137062306a36Sopenharmony_ci	set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
137162306a36Sopenharmony_ci	nfs_expire_all_delegations(clp);
137262306a36Sopenharmony_ci	dprintk("%s: handling CB_PATHDOWN recovery for server %s\n", __func__,
137362306a36Sopenharmony_ci			clp->cl_hostname);
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_civoid nfs4_schedule_path_down_recovery(struct nfs_client *clp)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	nfs40_handle_cb_pathdown(clp);
137962306a36Sopenharmony_ci	nfs4_schedule_state_manager(clp);
138062306a36Sopenharmony_ci}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_cistatic int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
138362306a36Sopenharmony_ci{
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	if (!nfs4_valid_open_stateid(state))
138662306a36Sopenharmony_ci		return 0;
138762306a36Sopenharmony_ci	set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
138862306a36Sopenharmony_ci	/* Don't recover state that expired before the reboot */
138962306a36Sopenharmony_ci	if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) {
139062306a36Sopenharmony_ci		clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
139162306a36Sopenharmony_ci		return 0;
139262306a36Sopenharmony_ci	}
139362306a36Sopenharmony_ci	set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags);
139462306a36Sopenharmony_ci	set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
139562306a36Sopenharmony_ci	return 1;
139662306a36Sopenharmony_ci}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ciint nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
139962306a36Sopenharmony_ci{
140062306a36Sopenharmony_ci	if (!nfs4_valid_open_stateid(state))
140162306a36Sopenharmony_ci		return 0;
140262306a36Sopenharmony_ci	set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
140362306a36Sopenharmony_ci	clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
140462306a36Sopenharmony_ci	set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags);
140562306a36Sopenharmony_ci	set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
140662306a36Sopenharmony_ci	return 1;
140762306a36Sopenharmony_ci}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ciint nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state)
141062306a36Sopenharmony_ci{
141162306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	if (!nfs4_state_mark_reclaim_nograce(clp, state))
141462306a36Sopenharmony_ci		return -EBADF;
141562306a36Sopenharmony_ci	nfs_inode_find_delegation_state_and_recover(state->inode,
141662306a36Sopenharmony_ci			&state->stateid);
141762306a36Sopenharmony_ci	dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
141862306a36Sopenharmony_ci			clp->cl_hostname);
141962306a36Sopenharmony_ci	nfs4_schedule_state_manager(clp);
142062306a36Sopenharmony_ci	return 0;
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery);
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_cistatic struct nfs4_lock_state *
142562306a36Sopenharmony_cinfs_state_find_lock_state_by_stateid(struct nfs4_state *state,
142662306a36Sopenharmony_ci		const nfs4_stateid *stateid)
142762306a36Sopenharmony_ci{
142862306a36Sopenharmony_ci	struct nfs4_lock_state *pos;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	list_for_each_entry(pos, &state->lock_states, ls_locks) {
143162306a36Sopenharmony_ci		if (!test_bit(NFS_LOCK_INITIALIZED, &pos->ls_flags))
143262306a36Sopenharmony_ci			continue;
143362306a36Sopenharmony_ci		if (nfs4_stateid_match_or_older(&pos->ls_stateid, stateid))
143462306a36Sopenharmony_ci			return pos;
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci	return NULL;
143762306a36Sopenharmony_ci}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_cistatic bool nfs_state_lock_state_matches_stateid(struct nfs4_state *state,
144062306a36Sopenharmony_ci		const nfs4_stateid *stateid)
144162306a36Sopenharmony_ci{
144262306a36Sopenharmony_ci	bool found = false;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	if (test_bit(LK_STATE_IN_USE, &state->flags)) {
144562306a36Sopenharmony_ci		spin_lock(&state->state_lock);
144662306a36Sopenharmony_ci		if (nfs_state_find_lock_state_by_stateid(state, stateid))
144762306a36Sopenharmony_ci			found = true;
144862306a36Sopenharmony_ci		spin_unlock(&state->state_lock);
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci	return found;
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_civoid nfs_inode_find_state_and_recover(struct inode *inode,
145462306a36Sopenharmony_ci		const nfs4_stateid *stateid)
145562306a36Sopenharmony_ci{
145662306a36Sopenharmony_ci	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
145762306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
145862306a36Sopenharmony_ci	struct nfs_open_context *ctx;
145962306a36Sopenharmony_ci	struct nfs4_state *state;
146062306a36Sopenharmony_ci	bool found = false;
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	rcu_read_lock();
146362306a36Sopenharmony_ci	list_for_each_entry_rcu(ctx, &nfsi->open_files, list) {
146462306a36Sopenharmony_ci		state = ctx->state;
146562306a36Sopenharmony_ci		if (state == NULL)
146662306a36Sopenharmony_ci			continue;
146762306a36Sopenharmony_ci		if (nfs4_stateid_match_or_older(&state->stateid, stateid) &&
146862306a36Sopenharmony_ci		    nfs4_state_mark_reclaim_nograce(clp, state)) {
146962306a36Sopenharmony_ci			found = true;
147062306a36Sopenharmony_ci			continue;
147162306a36Sopenharmony_ci		}
147262306a36Sopenharmony_ci		if (test_bit(NFS_OPEN_STATE, &state->flags) &&
147362306a36Sopenharmony_ci		    nfs4_stateid_match_or_older(&state->open_stateid, stateid) &&
147462306a36Sopenharmony_ci		    nfs4_state_mark_reclaim_nograce(clp, state)) {
147562306a36Sopenharmony_ci			found = true;
147662306a36Sopenharmony_ci			continue;
147762306a36Sopenharmony_ci		}
147862306a36Sopenharmony_ci		if (nfs_state_lock_state_matches_stateid(state, stateid) &&
147962306a36Sopenharmony_ci		    nfs4_state_mark_reclaim_nograce(clp, state))
148062306a36Sopenharmony_ci			found = true;
148162306a36Sopenharmony_ci	}
148262306a36Sopenharmony_ci	rcu_read_unlock();
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	nfs_inode_find_delegation_state_and_recover(inode, stateid);
148562306a36Sopenharmony_ci	if (found)
148662306a36Sopenharmony_ci		nfs4_schedule_state_manager(clp);
148762306a36Sopenharmony_ci}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_cistatic void nfs4_state_mark_open_context_bad(struct nfs4_state *state, int err)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	struct inode *inode = state->inode;
149262306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
149362306a36Sopenharmony_ci	struct nfs_open_context *ctx;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	rcu_read_lock();
149662306a36Sopenharmony_ci	list_for_each_entry_rcu(ctx, &nfsi->open_files, list) {
149762306a36Sopenharmony_ci		if (ctx->state != state)
149862306a36Sopenharmony_ci			continue;
149962306a36Sopenharmony_ci		set_bit(NFS_CONTEXT_BAD, &ctx->flags);
150062306a36Sopenharmony_ci		pr_warn("NFSv4: state recovery failed for open file %pd2, "
150162306a36Sopenharmony_ci				"error = %d\n", ctx->dentry, err);
150262306a36Sopenharmony_ci	}
150362306a36Sopenharmony_ci	rcu_read_unlock();
150462306a36Sopenharmony_ci}
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_cistatic void nfs4_state_mark_recovery_failed(struct nfs4_state *state, int error)
150762306a36Sopenharmony_ci{
150862306a36Sopenharmony_ci	set_bit(NFS_STATE_RECOVERY_FAILED, &state->flags);
150962306a36Sopenharmony_ci	nfs4_state_mark_open_context_bad(state, error);
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	struct inode *inode = state->inode;
151662306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
151762306a36Sopenharmony_ci	struct file_lock *fl;
151862306a36Sopenharmony_ci	struct nfs4_lock_state *lsp;
151962306a36Sopenharmony_ci	int status = 0;
152062306a36Sopenharmony_ci	struct file_lock_context *flctx = locks_inode_context(inode);
152162306a36Sopenharmony_ci	struct list_head *list;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (flctx == NULL)
152462306a36Sopenharmony_ci		return 0;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	list = &flctx->flc_posix;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	/* Guard against delegation returns and new lock/unlock calls */
152962306a36Sopenharmony_ci	down_write(&nfsi->rwsem);
153062306a36Sopenharmony_ci	spin_lock(&flctx->flc_lock);
153162306a36Sopenharmony_cirestart:
153262306a36Sopenharmony_ci	list_for_each_entry(fl, list, fl_list) {
153362306a36Sopenharmony_ci		if (nfs_file_open_context(fl->fl_file)->state != state)
153462306a36Sopenharmony_ci			continue;
153562306a36Sopenharmony_ci		spin_unlock(&flctx->flc_lock);
153662306a36Sopenharmony_ci		status = ops->recover_lock(state, fl);
153762306a36Sopenharmony_ci		switch (status) {
153862306a36Sopenharmony_ci		case 0:
153962306a36Sopenharmony_ci			break;
154062306a36Sopenharmony_ci		case -ETIMEDOUT:
154162306a36Sopenharmony_ci		case -ESTALE:
154262306a36Sopenharmony_ci		case -NFS4ERR_ADMIN_REVOKED:
154362306a36Sopenharmony_ci		case -NFS4ERR_STALE_STATEID:
154462306a36Sopenharmony_ci		case -NFS4ERR_BAD_STATEID:
154562306a36Sopenharmony_ci		case -NFS4ERR_EXPIRED:
154662306a36Sopenharmony_ci		case -NFS4ERR_NO_GRACE:
154762306a36Sopenharmony_ci		case -NFS4ERR_STALE_CLIENTID:
154862306a36Sopenharmony_ci		case -NFS4ERR_BADSESSION:
154962306a36Sopenharmony_ci		case -NFS4ERR_BADSLOT:
155062306a36Sopenharmony_ci		case -NFS4ERR_BAD_HIGH_SLOT:
155162306a36Sopenharmony_ci		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
155262306a36Sopenharmony_ci			goto out;
155362306a36Sopenharmony_ci		default:
155462306a36Sopenharmony_ci			pr_err("NFS: %s: unhandled error %d\n",
155562306a36Sopenharmony_ci					__func__, status);
155662306a36Sopenharmony_ci			fallthrough;
155762306a36Sopenharmony_ci		case -ENOMEM:
155862306a36Sopenharmony_ci		case -NFS4ERR_DENIED:
155962306a36Sopenharmony_ci		case -NFS4ERR_RECLAIM_BAD:
156062306a36Sopenharmony_ci		case -NFS4ERR_RECLAIM_CONFLICT:
156162306a36Sopenharmony_ci			lsp = fl->fl_u.nfs4_fl.owner;
156262306a36Sopenharmony_ci			if (lsp)
156362306a36Sopenharmony_ci				set_bit(NFS_LOCK_LOST, &lsp->ls_flags);
156462306a36Sopenharmony_ci			status = 0;
156562306a36Sopenharmony_ci		}
156662306a36Sopenharmony_ci		spin_lock(&flctx->flc_lock);
156762306a36Sopenharmony_ci	}
156862306a36Sopenharmony_ci	if (list == &flctx->flc_posix) {
156962306a36Sopenharmony_ci		list = &flctx->flc_flock;
157062306a36Sopenharmony_ci		goto restart;
157162306a36Sopenharmony_ci	}
157262306a36Sopenharmony_ci	spin_unlock(&flctx->flc_lock);
157362306a36Sopenharmony_ciout:
157462306a36Sopenharmony_ci	up_write(&nfsi->rwsem);
157562306a36Sopenharmony_ci	return status;
157662306a36Sopenharmony_ci}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
157962306a36Sopenharmony_cistatic void nfs42_complete_copies(struct nfs4_state_owner *sp, struct nfs4_state *state)
158062306a36Sopenharmony_ci{
158162306a36Sopenharmony_ci	struct nfs4_copy_state *copy;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	if (!test_bit(NFS_CLNT_DST_SSC_COPY_STATE, &state->flags) &&
158462306a36Sopenharmony_ci		!test_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags))
158562306a36Sopenharmony_ci		return;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	spin_lock(&sp->so_server->nfs_client->cl_lock);
158862306a36Sopenharmony_ci	list_for_each_entry(copy, &sp->so_server->ss_copies, copies) {
158962306a36Sopenharmony_ci		if ((test_bit(NFS_CLNT_DST_SSC_COPY_STATE, &state->flags) &&
159062306a36Sopenharmony_ci				!nfs4_stateid_match_other(&state->stateid,
159162306a36Sopenharmony_ci				&copy->parent_dst_state->stateid)))
159262306a36Sopenharmony_ci				continue;
159362306a36Sopenharmony_ci		copy->flags = 1;
159462306a36Sopenharmony_ci		if (test_and_clear_bit(NFS_CLNT_DST_SSC_COPY_STATE,
159562306a36Sopenharmony_ci				&state->flags)) {
159662306a36Sopenharmony_ci			clear_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags);
159762306a36Sopenharmony_ci			complete(&copy->completion);
159862306a36Sopenharmony_ci		}
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci	list_for_each_entry(copy, &sp->so_server->ss_copies, src_copies) {
160162306a36Sopenharmony_ci		if ((test_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags) &&
160262306a36Sopenharmony_ci				!nfs4_stateid_match_other(&state->stateid,
160362306a36Sopenharmony_ci				&copy->parent_src_state->stateid)))
160462306a36Sopenharmony_ci				continue;
160562306a36Sopenharmony_ci		copy->flags = 1;
160662306a36Sopenharmony_ci		if (test_and_clear_bit(NFS_CLNT_DST_SSC_COPY_STATE,
160762306a36Sopenharmony_ci				&state->flags))
160862306a36Sopenharmony_ci			complete(&copy->completion);
160962306a36Sopenharmony_ci	}
161062306a36Sopenharmony_ci	spin_unlock(&sp->so_server->nfs_client->cl_lock);
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci#else /* !CONFIG_NFS_V4_2 */
161362306a36Sopenharmony_cistatic inline void nfs42_complete_copies(struct nfs4_state_owner *sp,
161462306a36Sopenharmony_ci					 struct nfs4_state *state)
161562306a36Sopenharmony_ci{
161662306a36Sopenharmony_ci}
161762306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_cistatic int __nfs4_reclaim_open_state(struct nfs4_state_owner *sp, struct nfs4_state *state,
162062306a36Sopenharmony_ci				     const struct nfs4_state_recovery_ops *ops,
162162306a36Sopenharmony_ci				     int *lost_locks)
162262306a36Sopenharmony_ci{
162362306a36Sopenharmony_ci	struct nfs4_lock_state *lock;
162462306a36Sopenharmony_ci	int status;
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	status = ops->recover_open(sp, state);
162762306a36Sopenharmony_ci	if (status < 0)
162862306a36Sopenharmony_ci		return status;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	status = nfs4_reclaim_locks(state, ops);
163162306a36Sopenharmony_ci	if (status < 0)
163262306a36Sopenharmony_ci		return status;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) {
163562306a36Sopenharmony_ci		spin_lock(&state->state_lock);
163662306a36Sopenharmony_ci		list_for_each_entry(lock, &state->lock_states, ls_locks) {
163762306a36Sopenharmony_ci			trace_nfs4_state_lock_reclaim(state, lock);
163862306a36Sopenharmony_ci			if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags) &&
163962306a36Sopenharmony_ci			    !test_bit(NFS_LOCK_UNLOCKING, &lock->ls_flags))
164062306a36Sopenharmony_ci				*lost_locks += 1;
164162306a36Sopenharmony_ci		}
164262306a36Sopenharmony_ci		spin_unlock(&state->state_lock);
164362306a36Sopenharmony_ci	}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	nfs42_complete_copies(sp, state);
164662306a36Sopenharmony_ci	clear_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
164762306a36Sopenharmony_ci	return status;
164862306a36Sopenharmony_ci}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistatic int nfs4_reclaim_open_state(struct nfs4_state_owner *sp,
165162306a36Sopenharmony_ci				   const struct nfs4_state_recovery_ops *ops,
165262306a36Sopenharmony_ci				   int *lost_locks)
165362306a36Sopenharmony_ci{
165462306a36Sopenharmony_ci	struct nfs4_state *state;
165562306a36Sopenharmony_ci	unsigned int loop = 0;
165662306a36Sopenharmony_ci	int status = 0;
165762306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
165862306a36Sopenharmony_ci	bool found_ssc_copy_state = false;
165962306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	/* Note: we rely on the sp->so_states list being ordered
166262306a36Sopenharmony_ci	 * so that we always reclaim open(O_RDWR) and/or open(O_WRITE)
166362306a36Sopenharmony_ci	 * states first.
166462306a36Sopenharmony_ci	 * This is needed to ensure that the server won't give us any
166562306a36Sopenharmony_ci	 * read delegations that we have to return if, say, we are
166662306a36Sopenharmony_ci	 * recovering after a network partition or a reboot from a
166762306a36Sopenharmony_ci	 * server that doesn't support a grace period.
166862306a36Sopenharmony_ci	 */
166962306a36Sopenharmony_ci	spin_lock(&sp->so_lock);
167062306a36Sopenharmony_ci	raw_write_seqcount_begin(&sp->so_reclaim_seqcount);
167162306a36Sopenharmony_cirestart:
167262306a36Sopenharmony_ci	list_for_each_entry(state, &sp->so_states, open_states) {
167362306a36Sopenharmony_ci		if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
167462306a36Sopenharmony_ci			continue;
167562306a36Sopenharmony_ci		if (!nfs4_valid_open_stateid(state))
167662306a36Sopenharmony_ci			continue;
167762306a36Sopenharmony_ci		if (state->state == 0)
167862306a36Sopenharmony_ci			continue;
167962306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
168062306a36Sopenharmony_ci		if (test_bit(NFS_SRV_SSC_COPY_STATE, &state->flags)) {
168162306a36Sopenharmony_ci			nfs4_state_mark_recovery_failed(state, -EIO);
168262306a36Sopenharmony_ci			found_ssc_copy_state = true;
168362306a36Sopenharmony_ci			continue;
168462306a36Sopenharmony_ci		}
168562306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */
168662306a36Sopenharmony_ci		refcount_inc(&state->count);
168762306a36Sopenharmony_ci		spin_unlock(&sp->so_lock);
168862306a36Sopenharmony_ci		status = __nfs4_reclaim_open_state(sp, state, ops, lost_locks);
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci		switch (status) {
169162306a36Sopenharmony_ci		default:
169262306a36Sopenharmony_ci			if (status >= 0) {
169362306a36Sopenharmony_ci				loop = 0;
169462306a36Sopenharmony_ci				break;
169562306a36Sopenharmony_ci			}
169662306a36Sopenharmony_ci			printk(KERN_ERR "NFS: %s: unhandled error %d\n", __func__, status);
169762306a36Sopenharmony_ci			fallthrough;
169862306a36Sopenharmony_ci		case -ENOENT:
169962306a36Sopenharmony_ci		case -ENOMEM:
170062306a36Sopenharmony_ci		case -EACCES:
170162306a36Sopenharmony_ci		case -EROFS:
170262306a36Sopenharmony_ci		case -EIO:
170362306a36Sopenharmony_ci		case -ESTALE:
170462306a36Sopenharmony_ci			/* Open state on this file cannot be recovered */
170562306a36Sopenharmony_ci			nfs4_state_mark_recovery_failed(state, status);
170662306a36Sopenharmony_ci			break;
170762306a36Sopenharmony_ci		case -EAGAIN:
170862306a36Sopenharmony_ci			ssleep(1);
170962306a36Sopenharmony_ci			if (loop++ < 10) {
171062306a36Sopenharmony_ci				set_bit(ops->state_flag_bit, &state->flags);
171162306a36Sopenharmony_ci				break;
171262306a36Sopenharmony_ci			}
171362306a36Sopenharmony_ci			fallthrough;
171462306a36Sopenharmony_ci		case -NFS4ERR_ADMIN_REVOKED:
171562306a36Sopenharmony_ci		case -NFS4ERR_STALE_STATEID:
171662306a36Sopenharmony_ci		case -NFS4ERR_OLD_STATEID:
171762306a36Sopenharmony_ci		case -NFS4ERR_BAD_STATEID:
171862306a36Sopenharmony_ci		case -NFS4ERR_RECLAIM_BAD:
171962306a36Sopenharmony_ci		case -NFS4ERR_RECLAIM_CONFLICT:
172062306a36Sopenharmony_ci			nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state);
172162306a36Sopenharmony_ci			break;
172262306a36Sopenharmony_ci		case -NFS4ERR_EXPIRED:
172362306a36Sopenharmony_ci		case -NFS4ERR_NO_GRACE:
172462306a36Sopenharmony_ci			nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state);
172562306a36Sopenharmony_ci			fallthrough;
172662306a36Sopenharmony_ci		case -NFS4ERR_STALE_CLIENTID:
172762306a36Sopenharmony_ci		case -NFS4ERR_BADSESSION:
172862306a36Sopenharmony_ci		case -NFS4ERR_BADSLOT:
172962306a36Sopenharmony_ci		case -NFS4ERR_BAD_HIGH_SLOT:
173062306a36Sopenharmony_ci		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
173162306a36Sopenharmony_ci		case -ETIMEDOUT:
173262306a36Sopenharmony_ci			goto out_err;
173362306a36Sopenharmony_ci		}
173462306a36Sopenharmony_ci		nfs4_put_open_state(state);
173562306a36Sopenharmony_ci		spin_lock(&sp->so_lock);
173662306a36Sopenharmony_ci		goto restart;
173762306a36Sopenharmony_ci	}
173862306a36Sopenharmony_ci	raw_write_seqcount_end(&sp->so_reclaim_seqcount);
173962306a36Sopenharmony_ci	spin_unlock(&sp->so_lock);
174062306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
174162306a36Sopenharmony_ci	if (found_ssc_copy_state)
174262306a36Sopenharmony_ci		return -EIO;
174362306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */
174462306a36Sopenharmony_ci	return 0;
174562306a36Sopenharmony_ciout_err:
174662306a36Sopenharmony_ci	nfs4_put_open_state(state);
174762306a36Sopenharmony_ci	spin_lock(&sp->so_lock);
174862306a36Sopenharmony_ci	raw_write_seqcount_end(&sp->so_reclaim_seqcount);
174962306a36Sopenharmony_ci	spin_unlock(&sp->so_lock);
175062306a36Sopenharmony_ci	return status;
175162306a36Sopenharmony_ci}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_cistatic void nfs4_clear_open_state(struct nfs4_state *state)
175462306a36Sopenharmony_ci{
175562306a36Sopenharmony_ci	struct nfs4_lock_state *lock;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	clear_bit(NFS_DELEGATED_STATE, &state->flags);
175862306a36Sopenharmony_ci	clear_bit(NFS_O_RDONLY_STATE, &state->flags);
175962306a36Sopenharmony_ci	clear_bit(NFS_O_WRONLY_STATE, &state->flags);
176062306a36Sopenharmony_ci	clear_bit(NFS_O_RDWR_STATE, &state->flags);
176162306a36Sopenharmony_ci	spin_lock(&state->state_lock);
176262306a36Sopenharmony_ci	list_for_each_entry(lock, &state->lock_states, ls_locks) {
176362306a36Sopenharmony_ci		lock->ls_seqid.flags = 0;
176462306a36Sopenharmony_ci		clear_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags);
176562306a36Sopenharmony_ci	}
176662306a36Sopenharmony_ci	spin_unlock(&state->state_lock);
176762306a36Sopenharmony_ci}
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_cistatic void nfs4_reset_seqids(struct nfs_server *server,
177062306a36Sopenharmony_ci	int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
177162306a36Sopenharmony_ci{
177262306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
177362306a36Sopenharmony_ci	struct nfs4_state_owner *sp;
177462306a36Sopenharmony_ci	struct rb_node *pos;
177562306a36Sopenharmony_ci	struct nfs4_state *state;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
177862306a36Sopenharmony_ci	for (pos = rb_first(&server->state_owners);
177962306a36Sopenharmony_ci	     pos != NULL;
178062306a36Sopenharmony_ci	     pos = rb_next(pos)) {
178162306a36Sopenharmony_ci		sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
178262306a36Sopenharmony_ci		sp->so_seqid.flags = 0;
178362306a36Sopenharmony_ci		spin_lock(&sp->so_lock);
178462306a36Sopenharmony_ci		list_for_each_entry(state, &sp->so_states, open_states) {
178562306a36Sopenharmony_ci			if (mark_reclaim(clp, state))
178662306a36Sopenharmony_ci				nfs4_clear_open_state(state);
178762306a36Sopenharmony_ci		}
178862306a36Sopenharmony_ci		spin_unlock(&sp->so_lock);
178962306a36Sopenharmony_ci	}
179062306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
179162306a36Sopenharmony_ci}
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_cistatic void nfs4_state_mark_reclaim_helper(struct nfs_client *clp,
179462306a36Sopenharmony_ci	int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
179562306a36Sopenharmony_ci{
179662306a36Sopenharmony_ci	struct nfs_server *server;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	rcu_read_lock();
179962306a36Sopenharmony_ci	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
180062306a36Sopenharmony_ci		nfs4_reset_seqids(server, mark_reclaim);
180162306a36Sopenharmony_ci	rcu_read_unlock();
180262306a36Sopenharmony_ci}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_cistatic void nfs4_state_start_reclaim_reboot(struct nfs_client *clp)
180562306a36Sopenharmony_ci{
180662306a36Sopenharmony_ci	set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
180762306a36Sopenharmony_ci	/* Mark all delegations for reclaim */
180862306a36Sopenharmony_ci	nfs_delegation_mark_reclaim(clp);
180962306a36Sopenharmony_ci	nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot);
181062306a36Sopenharmony_ci}
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_cistatic int nfs4_reclaim_complete(struct nfs_client *clp,
181362306a36Sopenharmony_ci				 const struct nfs4_state_recovery_ops *ops,
181462306a36Sopenharmony_ci				 const struct cred *cred)
181562306a36Sopenharmony_ci{
181662306a36Sopenharmony_ci	/* Notify the server we're done reclaiming our state */
181762306a36Sopenharmony_ci	if (ops->reclaim_complete)
181862306a36Sopenharmony_ci		return ops->reclaim_complete(clp, cred);
181962306a36Sopenharmony_ci	return 0;
182062306a36Sopenharmony_ci}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_cistatic void nfs4_clear_reclaim_server(struct nfs_server *server)
182362306a36Sopenharmony_ci{
182462306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
182562306a36Sopenharmony_ci	struct nfs4_state_owner *sp;
182662306a36Sopenharmony_ci	struct rb_node *pos;
182762306a36Sopenharmony_ci	struct nfs4_state *state;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	spin_lock(&clp->cl_lock);
183062306a36Sopenharmony_ci	for (pos = rb_first(&server->state_owners);
183162306a36Sopenharmony_ci	     pos != NULL;
183262306a36Sopenharmony_ci	     pos = rb_next(pos)) {
183362306a36Sopenharmony_ci		sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
183462306a36Sopenharmony_ci		spin_lock(&sp->so_lock);
183562306a36Sopenharmony_ci		list_for_each_entry(state, &sp->so_states, open_states) {
183662306a36Sopenharmony_ci			if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT,
183762306a36Sopenharmony_ci						&state->flags))
183862306a36Sopenharmony_ci				continue;
183962306a36Sopenharmony_ci			nfs4_state_mark_reclaim_nograce(clp, state);
184062306a36Sopenharmony_ci		}
184162306a36Sopenharmony_ci		spin_unlock(&sp->so_lock);
184262306a36Sopenharmony_ci	}
184362306a36Sopenharmony_ci	spin_unlock(&clp->cl_lock);
184462306a36Sopenharmony_ci}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp)
184762306a36Sopenharmony_ci{
184862306a36Sopenharmony_ci	struct nfs_server *server;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
185162306a36Sopenharmony_ci		return 0;
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	rcu_read_lock();
185462306a36Sopenharmony_ci	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
185562306a36Sopenharmony_ci		nfs4_clear_reclaim_server(server);
185662306a36Sopenharmony_ci	rcu_read_unlock();
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	nfs_delegation_reap_unclaimed(clp);
185962306a36Sopenharmony_ci	return 1;
186062306a36Sopenharmony_ci}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_cistatic void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
186362306a36Sopenharmony_ci{
186462306a36Sopenharmony_ci	const struct nfs4_state_recovery_ops *ops;
186562306a36Sopenharmony_ci	const struct cred *cred;
186662306a36Sopenharmony_ci	int err;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	if (!nfs4_state_clear_reclaim_reboot(clp))
186962306a36Sopenharmony_ci		return;
187062306a36Sopenharmony_ci	ops = clp->cl_mvops->reboot_recovery_ops;
187162306a36Sopenharmony_ci	cred = nfs4_get_clid_cred(clp);
187262306a36Sopenharmony_ci	err = nfs4_reclaim_complete(clp, ops, cred);
187362306a36Sopenharmony_ci	put_cred(cred);
187462306a36Sopenharmony_ci	if (err == -NFS4ERR_CONN_NOT_BOUND_TO_SESSION)
187562306a36Sopenharmony_ci		set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
187662306a36Sopenharmony_ci}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_cistatic void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
187962306a36Sopenharmony_ci{
188062306a36Sopenharmony_ci	nfs_mark_test_expired_all_delegations(clp);
188162306a36Sopenharmony_ci	nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
188262306a36Sopenharmony_ci}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_cistatic int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
188562306a36Sopenharmony_ci{
188662306a36Sopenharmony_ci	switch (error) {
188762306a36Sopenharmony_ci	case 0:
188862306a36Sopenharmony_ci		break;
188962306a36Sopenharmony_ci	case -NFS4ERR_CB_PATH_DOWN:
189062306a36Sopenharmony_ci		nfs40_handle_cb_pathdown(clp);
189162306a36Sopenharmony_ci		break;
189262306a36Sopenharmony_ci	case -NFS4ERR_NO_GRACE:
189362306a36Sopenharmony_ci		nfs4_state_end_reclaim_reboot(clp);
189462306a36Sopenharmony_ci		break;
189562306a36Sopenharmony_ci	case -NFS4ERR_STALE_CLIENTID:
189662306a36Sopenharmony_ci		set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
189762306a36Sopenharmony_ci		nfs4_state_start_reclaim_reboot(clp);
189862306a36Sopenharmony_ci		break;
189962306a36Sopenharmony_ci	case -NFS4ERR_EXPIRED:
190062306a36Sopenharmony_ci		set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
190162306a36Sopenharmony_ci		nfs4_state_start_reclaim_nograce(clp);
190262306a36Sopenharmony_ci		break;
190362306a36Sopenharmony_ci	case -NFS4ERR_BADSESSION:
190462306a36Sopenharmony_ci	case -NFS4ERR_BADSLOT:
190562306a36Sopenharmony_ci	case -NFS4ERR_BAD_HIGH_SLOT:
190662306a36Sopenharmony_ci	case -NFS4ERR_DEADSESSION:
190762306a36Sopenharmony_ci	case -NFS4ERR_SEQ_FALSE_RETRY:
190862306a36Sopenharmony_ci	case -NFS4ERR_SEQ_MISORDERED:
190962306a36Sopenharmony_ci		set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
191062306a36Sopenharmony_ci		/* Zero session reset errors */
191162306a36Sopenharmony_ci		break;
191262306a36Sopenharmony_ci	case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
191362306a36Sopenharmony_ci		set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
191462306a36Sopenharmony_ci		break;
191562306a36Sopenharmony_ci	default:
191662306a36Sopenharmony_ci		dprintk("%s: failed to handle error %d for server %s\n",
191762306a36Sopenharmony_ci				__func__, error, clp->cl_hostname);
191862306a36Sopenharmony_ci		return error;
191962306a36Sopenharmony_ci	}
192062306a36Sopenharmony_ci	dprintk("%s: handled error %d for server %s\n", __func__, error,
192162306a36Sopenharmony_ci			clp->cl_hostname);
192262306a36Sopenharmony_ci	return 0;
192362306a36Sopenharmony_ci}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_cistatic int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
192662306a36Sopenharmony_ci{
192762306a36Sopenharmony_ci	struct nfs4_state_owner *sp;
192862306a36Sopenharmony_ci	struct nfs_server *server;
192962306a36Sopenharmony_ci	struct rb_node *pos;
193062306a36Sopenharmony_ci	LIST_HEAD(freeme);
193162306a36Sopenharmony_ci	int status = 0;
193262306a36Sopenharmony_ci	int lost_locks = 0;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_cirestart:
193562306a36Sopenharmony_ci	rcu_read_lock();
193662306a36Sopenharmony_ci	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
193762306a36Sopenharmony_ci		nfs4_purge_state_owners(server, &freeme);
193862306a36Sopenharmony_ci		spin_lock(&clp->cl_lock);
193962306a36Sopenharmony_ci		for (pos = rb_first(&server->state_owners);
194062306a36Sopenharmony_ci		     pos != NULL;
194162306a36Sopenharmony_ci		     pos = rb_next(pos)) {
194262306a36Sopenharmony_ci			sp = rb_entry(pos,
194362306a36Sopenharmony_ci				struct nfs4_state_owner, so_server_node);
194462306a36Sopenharmony_ci			if (!test_and_clear_bit(ops->owner_flag_bit,
194562306a36Sopenharmony_ci							&sp->so_flags))
194662306a36Sopenharmony_ci				continue;
194762306a36Sopenharmony_ci			if (!atomic_inc_not_zero(&sp->so_count))
194862306a36Sopenharmony_ci				continue;
194962306a36Sopenharmony_ci			spin_unlock(&clp->cl_lock);
195062306a36Sopenharmony_ci			rcu_read_unlock();
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci			status = nfs4_reclaim_open_state(sp, ops, &lost_locks);
195362306a36Sopenharmony_ci			if (status < 0) {
195462306a36Sopenharmony_ci				if (lost_locks)
195562306a36Sopenharmony_ci					pr_warn("NFS: %s: lost %d locks\n",
195662306a36Sopenharmony_ci						clp->cl_hostname, lost_locks);
195762306a36Sopenharmony_ci				set_bit(ops->owner_flag_bit, &sp->so_flags);
195862306a36Sopenharmony_ci				nfs4_put_state_owner(sp);
195962306a36Sopenharmony_ci				status = nfs4_recovery_handle_error(clp, status);
196062306a36Sopenharmony_ci				return (status != 0) ? status : -EAGAIN;
196162306a36Sopenharmony_ci			}
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci			nfs4_put_state_owner(sp);
196462306a36Sopenharmony_ci			goto restart;
196562306a36Sopenharmony_ci		}
196662306a36Sopenharmony_ci		spin_unlock(&clp->cl_lock);
196762306a36Sopenharmony_ci	}
196862306a36Sopenharmony_ci	rcu_read_unlock();
196962306a36Sopenharmony_ci	nfs4_free_state_owners(&freeme);
197062306a36Sopenharmony_ci	if (lost_locks)
197162306a36Sopenharmony_ci		pr_warn("NFS: %s: lost %d locks\n",
197262306a36Sopenharmony_ci			clp->cl_hostname, lost_locks);
197362306a36Sopenharmony_ci	return 0;
197462306a36Sopenharmony_ci}
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_cistatic int nfs4_check_lease(struct nfs_client *clp)
197762306a36Sopenharmony_ci{
197862306a36Sopenharmony_ci	const struct cred *cred;
197962306a36Sopenharmony_ci	const struct nfs4_state_maintenance_ops *ops =
198062306a36Sopenharmony_ci		clp->cl_mvops->state_renewal_ops;
198162306a36Sopenharmony_ci	int status;
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	/* Is the client already known to have an expired lease? */
198462306a36Sopenharmony_ci	if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
198562306a36Sopenharmony_ci		return 0;
198662306a36Sopenharmony_ci	cred = ops->get_state_renewal_cred(clp);
198762306a36Sopenharmony_ci	if (cred == NULL) {
198862306a36Sopenharmony_ci		cred = nfs4_get_clid_cred(clp);
198962306a36Sopenharmony_ci		status = -ENOKEY;
199062306a36Sopenharmony_ci		if (cred == NULL)
199162306a36Sopenharmony_ci			goto out;
199262306a36Sopenharmony_ci	}
199362306a36Sopenharmony_ci	status = ops->renew_lease(clp, cred);
199462306a36Sopenharmony_ci	put_cred(cred);
199562306a36Sopenharmony_ci	if (status == -ETIMEDOUT) {
199662306a36Sopenharmony_ci		set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
199762306a36Sopenharmony_ci		return 0;
199862306a36Sopenharmony_ci	}
199962306a36Sopenharmony_ciout:
200062306a36Sopenharmony_ci	return nfs4_recovery_handle_error(clp, status);
200162306a36Sopenharmony_ci}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci/* Set NFS4CLNT_LEASE_EXPIRED and reclaim reboot state for all v4.0 errors
200462306a36Sopenharmony_ci * and for recoverable errors on EXCHANGE_ID for v4.1
200562306a36Sopenharmony_ci */
200662306a36Sopenharmony_cistatic int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
200762306a36Sopenharmony_ci{
200862306a36Sopenharmony_ci	switch (status) {
200962306a36Sopenharmony_ci	case -NFS4ERR_SEQ_MISORDERED:
201062306a36Sopenharmony_ci		if (test_and_set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state))
201162306a36Sopenharmony_ci			return -ESERVERFAULT;
201262306a36Sopenharmony_ci		/* Lease confirmation error: retry after purging the lease */
201362306a36Sopenharmony_ci		ssleep(1);
201462306a36Sopenharmony_ci		clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
201562306a36Sopenharmony_ci		break;
201662306a36Sopenharmony_ci	case -NFS4ERR_STALE_CLIENTID:
201762306a36Sopenharmony_ci		clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
201862306a36Sopenharmony_ci		nfs4_state_start_reclaim_reboot(clp);
201962306a36Sopenharmony_ci		break;
202062306a36Sopenharmony_ci	case -NFS4ERR_CLID_INUSE:
202162306a36Sopenharmony_ci		pr_err("NFS: Server %s reports our clientid is in use\n",
202262306a36Sopenharmony_ci			clp->cl_hostname);
202362306a36Sopenharmony_ci		nfs_mark_client_ready(clp, -EPERM);
202462306a36Sopenharmony_ci		clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
202562306a36Sopenharmony_ci		return -EPERM;
202662306a36Sopenharmony_ci	case -EACCES:
202762306a36Sopenharmony_ci	case -NFS4ERR_DELAY:
202862306a36Sopenharmony_ci	case -EAGAIN:
202962306a36Sopenharmony_ci		ssleep(1);
203062306a36Sopenharmony_ci		break;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	case -NFS4ERR_MINOR_VERS_MISMATCH:
203362306a36Sopenharmony_ci		if (clp->cl_cons_state == NFS_CS_SESSION_INITING)
203462306a36Sopenharmony_ci			nfs_mark_client_ready(clp, -EPROTONOSUPPORT);
203562306a36Sopenharmony_ci		dprintk("%s: exit with error %d for server %s\n",
203662306a36Sopenharmony_ci				__func__, -EPROTONOSUPPORT, clp->cl_hostname);
203762306a36Sopenharmony_ci		return -EPROTONOSUPPORT;
203862306a36Sopenharmony_ci	case -ENOSPC:
203962306a36Sopenharmony_ci		if (clp->cl_cons_state == NFS_CS_SESSION_INITING)
204062306a36Sopenharmony_ci			nfs_mark_client_ready(clp, -EIO);
204162306a36Sopenharmony_ci		return -EIO;
204262306a36Sopenharmony_ci	case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
204362306a36Sopenharmony_ci				 * in nfs4_exchange_id */
204462306a36Sopenharmony_ci	default:
204562306a36Sopenharmony_ci		dprintk("%s: exit with error %d for server %s\n", __func__,
204662306a36Sopenharmony_ci				status, clp->cl_hostname);
204762306a36Sopenharmony_ci		return status;
204862306a36Sopenharmony_ci	}
204962306a36Sopenharmony_ci	set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
205062306a36Sopenharmony_ci	dprintk("%s: handled error %d for server %s\n", __func__, status,
205162306a36Sopenharmony_ci			clp->cl_hostname);
205262306a36Sopenharmony_ci	return 0;
205362306a36Sopenharmony_ci}
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_cistatic int nfs4_establish_lease(struct nfs_client *clp)
205662306a36Sopenharmony_ci{
205762306a36Sopenharmony_ci	const struct cred *cred;
205862306a36Sopenharmony_ci	const struct nfs4_state_recovery_ops *ops =
205962306a36Sopenharmony_ci		clp->cl_mvops->reboot_recovery_ops;
206062306a36Sopenharmony_ci	int status;
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	status = nfs4_begin_drain_session(clp);
206362306a36Sopenharmony_ci	if (status != 0)
206462306a36Sopenharmony_ci		return status;
206562306a36Sopenharmony_ci	cred = nfs4_get_clid_cred(clp);
206662306a36Sopenharmony_ci	if (cred == NULL)
206762306a36Sopenharmony_ci		return -ENOENT;
206862306a36Sopenharmony_ci	status = ops->establish_clid(clp, cred);
206962306a36Sopenharmony_ci	put_cred(cred);
207062306a36Sopenharmony_ci	if (status != 0)
207162306a36Sopenharmony_ci		return status;
207262306a36Sopenharmony_ci	pnfs_destroy_all_layouts(clp);
207362306a36Sopenharmony_ci	return 0;
207462306a36Sopenharmony_ci}
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci/*
207762306a36Sopenharmony_ci * Returns zero or a negative errno.  NFS4ERR values are converted
207862306a36Sopenharmony_ci * to local errno values.
207962306a36Sopenharmony_ci */
208062306a36Sopenharmony_cistatic int nfs4_reclaim_lease(struct nfs_client *clp)
208162306a36Sopenharmony_ci{
208262306a36Sopenharmony_ci	int status;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	status = nfs4_establish_lease(clp);
208562306a36Sopenharmony_ci	if (status < 0)
208662306a36Sopenharmony_ci		return nfs4_handle_reclaim_lease_error(clp, status);
208762306a36Sopenharmony_ci	if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state))
208862306a36Sopenharmony_ci		nfs4_state_start_reclaim_nograce(clp);
208962306a36Sopenharmony_ci	if (!test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state))
209062306a36Sopenharmony_ci		set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
209162306a36Sopenharmony_ci	clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
209262306a36Sopenharmony_ci	clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
209362306a36Sopenharmony_ci	return 0;
209462306a36Sopenharmony_ci}
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_cistatic int nfs4_purge_lease(struct nfs_client *clp)
209762306a36Sopenharmony_ci{
209862306a36Sopenharmony_ci	int status;
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	status = nfs4_establish_lease(clp);
210162306a36Sopenharmony_ci	if (status < 0)
210262306a36Sopenharmony_ci		return nfs4_handle_reclaim_lease_error(clp, status);
210362306a36Sopenharmony_ci	clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
210462306a36Sopenharmony_ci	set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
210562306a36Sopenharmony_ci	nfs4_state_start_reclaim_nograce(clp);
210662306a36Sopenharmony_ci	return 0;
210762306a36Sopenharmony_ci}
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci/*
211062306a36Sopenharmony_ci * Try remote migration of one FSID from a source server to a
211162306a36Sopenharmony_ci * destination server.  The source server provides a list of
211262306a36Sopenharmony_ci * potential destinations.
211362306a36Sopenharmony_ci *
211462306a36Sopenharmony_ci * Returns zero or a negative NFS4ERR status code.
211562306a36Sopenharmony_ci */
211662306a36Sopenharmony_cistatic int nfs4_try_migration(struct nfs_server *server, const struct cred *cred)
211762306a36Sopenharmony_ci{
211862306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
211962306a36Sopenharmony_ci	struct nfs4_fs_locations *locations = NULL;
212062306a36Sopenharmony_ci	struct inode *inode;
212162306a36Sopenharmony_ci	struct page *page;
212262306a36Sopenharmony_ci	int status, result;
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	dprintk("--> %s: FSID %llx:%llx on \"%s\"\n", __func__,
212562306a36Sopenharmony_ci			(unsigned long long)server->fsid.major,
212662306a36Sopenharmony_ci			(unsigned long long)server->fsid.minor,
212762306a36Sopenharmony_ci			clp->cl_hostname);
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	result = 0;
213062306a36Sopenharmony_ci	page = alloc_page(GFP_KERNEL);
213162306a36Sopenharmony_ci	locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
213262306a36Sopenharmony_ci	if (page == NULL || locations == NULL) {
213362306a36Sopenharmony_ci		dprintk("<-- %s: no memory\n", __func__);
213462306a36Sopenharmony_ci		goto out;
213562306a36Sopenharmony_ci	}
213662306a36Sopenharmony_ci	locations->fattr = nfs_alloc_fattr();
213762306a36Sopenharmony_ci	if (locations->fattr == NULL) {
213862306a36Sopenharmony_ci		dprintk("<-- %s: no memory\n", __func__);
213962306a36Sopenharmony_ci		goto out;
214062306a36Sopenharmony_ci	}
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_ci	inode = d_inode(server->super->s_root);
214362306a36Sopenharmony_ci	result = nfs4_proc_get_locations(server, NFS_FH(inode), locations,
214462306a36Sopenharmony_ci					 page, cred);
214562306a36Sopenharmony_ci	if (result) {
214662306a36Sopenharmony_ci		dprintk("<-- %s: failed to retrieve fs_locations: %d\n",
214762306a36Sopenharmony_ci			__func__, result);
214862306a36Sopenharmony_ci		goto out;
214962306a36Sopenharmony_ci	}
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	result = -NFS4ERR_NXIO;
215262306a36Sopenharmony_ci	if (!locations->nlocations)
215362306a36Sopenharmony_ci		goto out;
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci	if (!(locations->fattr->valid & NFS_ATTR_FATTR_V4_LOCATIONS)) {
215662306a36Sopenharmony_ci		dprintk("<-- %s: No fs_locations data, migration skipped\n",
215762306a36Sopenharmony_ci			__func__);
215862306a36Sopenharmony_ci		goto out;
215962306a36Sopenharmony_ci	}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	status = nfs4_begin_drain_session(clp);
216262306a36Sopenharmony_ci	if (status != 0) {
216362306a36Sopenharmony_ci		result = status;
216462306a36Sopenharmony_ci		goto out;
216562306a36Sopenharmony_ci	}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	status = nfs4_replace_transport(server, locations);
216862306a36Sopenharmony_ci	if (status != 0) {
216962306a36Sopenharmony_ci		dprintk("<-- %s: failed to replace transport: %d\n",
217062306a36Sopenharmony_ci			__func__, status);
217162306a36Sopenharmony_ci		goto out;
217262306a36Sopenharmony_ci	}
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	result = 0;
217562306a36Sopenharmony_ci	dprintk("<-- %s: migration succeeded\n", __func__);
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ciout:
217862306a36Sopenharmony_ci	if (page != NULL)
217962306a36Sopenharmony_ci		__free_page(page);
218062306a36Sopenharmony_ci	if (locations != NULL)
218162306a36Sopenharmony_ci		kfree(locations->fattr);
218262306a36Sopenharmony_ci	kfree(locations);
218362306a36Sopenharmony_ci	if (result) {
218462306a36Sopenharmony_ci		pr_err("NFS: migration recovery failed (server %s)\n",
218562306a36Sopenharmony_ci				clp->cl_hostname);
218662306a36Sopenharmony_ci		set_bit(NFS_MIG_FAILED, &server->mig_status);
218762306a36Sopenharmony_ci	}
218862306a36Sopenharmony_ci	return result;
218962306a36Sopenharmony_ci}
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci/*
219262306a36Sopenharmony_ci * Returns zero or a negative NFS4ERR status code.
219362306a36Sopenharmony_ci */
219462306a36Sopenharmony_cistatic int nfs4_handle_migration(struct nfs_client *clp)
219562306a36Sopenharmony_ci{
219662306a36Sopenharmony_ci	const struct nfs4_state_maintenance_ops *ops =
219762306a36Sopenharmony_ci				clp->cl_mvops->state_renewal_ops;
219862306a36Sopenharmony_ci	struct nfs_server *server;
219962306a36Sopenharmony_ci	const struct cred *cred;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	dprintk("%s: migration reported on \"%s\"\n", __func__,
220262306a36Sopenharmony_ci			clp->cl_hostname);
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	cred = ops->get_state_renewal_cred(clp);
220562306a36Sopenharmony_ci	if (cred == NULL)
220662306a36Sopenharmony_ci		return -NFS4ERR_NOENT;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	clp->cl_mig_gen++;
220962306a36Sopenharmony_cirestart:
221062306a36Sopenharmony_ci	rcu_read_lock();
221162306a36Sopenharmony_ci	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
221262306a36Sopenharmony_ci		int status;
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci		if (server->mig_gen == clp->cl_mig_gen)
221562306a36Sopenharmony_ci			continue;
221662306a36Sopenharmony_ci		server->mig_gen = clp->cl_mig_gen;
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci		if (!test_and_clear_bit(NFS_MIG_IN_TRANSITION,
221962306a36Sopenharmony_ci						&server->mig_status))
222062306a36Sopenharmony_ci			continue;
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci		rcu_read_unlock();
222362306a36Sopenharmony_ci		status = nfs4_try_migration(server, cred);
222462306a36Sopenharmony_ci		if (status < 0) {
222562306a36Sopenharmony_ci			put_cred(cred);
222662306a36Sopenharmony_ci			return status;
222762306a36Sopenharmony_ci		}
222862306a36Sopenharmony_ci		goto restart;
222962306a36Sopenharmony_ci	}
223062306a36Sopenharmony_ci	rcu_read_unlock();
223162306a36Sopenharmony_ci	put_cred(cred);
223262306a36Sopenharmony_ci	return 0;
223362306a36Sopenharmony_ci}
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci/*
223662306a36Sopenharmony_ci * Test each nfs_server on the clp's cl_superblocks list to see
223762306a36Sopenharmony_ci * if it's moved to another server.  Stop when the server no longer
223862306a36Sopenharmony_ci * returns NFS4ERR_LEASE_MOVED.
223962306a36Sopenharmony_ci */
224062306a36Sopenharmony_cistatic int nfs4_handle_lease_moved(struct nfs_client *clp)
224162306a36Sopenharmony_ci{
224262306a36Sopenharmony_ci	const struct nfs4_state_maintenance_ops *ops =
224362306a36Sopenharmony_ci				clp->cl_mvops->state_renewal_ops;
224462306a36Sopenharmony_ci	struct nfs_server *server;
224562306a36Sopenharmony_ci	const struct cred *cred;
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	dprintk("%s: lease moved reported on \"%s\"\n", __func__,
224862306a36Sopenharmony_ci			clp->cl_hostname);
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	cred = ops->get_state_renewal_cred(clp);
225162306a36Sopenharmony_ci	if (cred == NULL)
225262306a36Sopenharmony_ci		return -NFS4ERR_NOENT;
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	clp->cl_mig_gen++;
225562306a36Sopenharmony_cirestart:
225662306a36Sopenharmony_ci	rcu_read_lock();
225762306a36Sopenharmony_ci	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
225862306a36Sopenharmony_ci		struct inode *inode;
225962306a36Sopenharmony_ci		int status;
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci		if (server->mig_gen == clp->cl_mig_gen)
226262306a36Sopenharmony_ci			continue;
226362306a36Sopenharmony_ci		server->mig_gen = clp->cl_mig_gen;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci		rcu_read_unlock();
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci		inode = d_inode(server->super->s_root);
226862306a36Sopenharmony_ci		status = nfs4_proc_fsid_present(inode, cred);
226962306a36Sopenharmony_ci		if (status != -NFS4ERR_MOVED)
227062306a36Sopenharmony_ci			goto restart;	/* wasn't this one */
227162306a36Sopenharmony_ci		if (nfs4_try_migration(server, cred) == -NFS4ERR_LEASE_MOVED)
227262306a36Sopenharmony_ci			goto restart;	/* there are more */
227362306a36Sopenharmony_ci		goto out;
227462306a36Sopenharmony_ci	}
227562306a36Sopenharmony_ci	rcu_read_unlock();
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ciout:
227862306a36Sopenharmony_ci	put_cred(cred);
227962306a36Sopenharmony_ci	return 0;
228062306a36Sopenharmony_ci}
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci/**
228362306a36Sopenharmony_ci * nfs4_discover_server_trunking - Detect server IP address trunking
228462306a36Sopenharmony_ci *
228562306a36Sopenharmony_ci * @clp: nfs_client under test
228662306a36Sopenharmony_ci * @result: OUT: found nfs_client, or clp
228762306a36Sopenharmony_ci *
228862306a36Sopenharmony_ci * Returns zero or a negative errno.  If zero is returned,
228962306a36Sopenharmony_ci * an nfs_client pointer is planted in "result".
229062306a36Sopenharmony_ci *
229162306a36Sopenharmony_ci * Note: since we are invoked in process context, and
229262306a36Sopenharmony_ci * not from inside the state manager, we cannot use
229362306a36Sopenharmony_ci * nfs4_handle_reclaim_lease_error().
229462306a36Sopenharmony_ci */
229562306a36Sopenharmony_ciint nfs4_discover_server_trunking(struct nfs_client *clp,
229662306a36Sopenharmony_ci				  struct nfs_client **result)
229762306a36Sopenharmony_ci{
229862306a36Sopenharmony_ci	const struct nfs4_state_recovery_ops *ops =
229962306a36Sopenharmony_ci				clp->cl_mvops->reboot_recovery_ops;
230062306a36Sopenharmony_ci	struct rpc_clnt *clnt;
230162306a36Sopenharmony_ci	const struct cred *cred;
230262306a36Sopenharmony_ci	int i, status;
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname);
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci	clnt = clp->cl_rpcclient;
230762306a36Sopenharmony_ci	i = 0;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	mutex_lock(&nfs_clid_init_mutex);
231062306a36Sopenharmony_ciagain:
231162306a36Sopenharmony_ci	status  = -ENOENT;
231262306a36Sopenharmony_ci	cred = nfs4_get_clid_cred(clp);
231362306a36Sopenharmony_ci	if (cred == NULL)
231462306a36Sopenharmony_ci		goto out_unlock;
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	status = ops->detect_trunking(clp, result, cred);
231762306a36Sopenharmony_ci	put_cred(cred);
231862306a36Sopenharmony_ci	switch (status) {
231962306a36Sopenharmony_ci	case 0:
232062306a36Sopenharmony_ci	case -EINTR:
232162306a36Sopenharmony_ci	case -ERESTARTSYS:
232262306a36Sopenharmony_ci		break;
232362306a36Sopenharmony_ci	case -ETIMEDOUT:
232462306a36Sopenharmony_ci		if (clnt->cl_softrtry)
232562306a36Sopenharmony_ci			break;
232662306a36Sopenharmony_ci		fallthrough;
232762306a36Sopenharmony_ci	case -NFS4ERR_DELAY:
232862306a36Sopenharmony_ci	case -EAGAIN:
232962306a36Sopenharmony_ci		ssleep(1);
233062306a36Sopenharmony_ci		fallthrough;
233162306a36Sopenharmony_ci	case -NFS4ERR_STALE_CLIENTID:
233262306a36Sopenharmony_ci		dprintk("NFS: %s after status %d, retrying\n",
233362306a36Sopenharmony_ci			__func__, status);
233462306a36Sopenharmony_ci		goto again;
233562306a36Sopenharmony_ci	case -EACCES:
233662306a36Sopenharmony_ci		if (i++ == 0) {
233762306a36Sopenharmony_ci			nfs4_root_machine_cred(clp);
233862306a36Sopenharmony_ci			goto again;
233962306a36Sopenharmony_ci		}
234062306a36Sopenharmony_ci		if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX)
234162306a36Sopenharmony_ci			break;
234262306a36Sopenharmony_ci		fallthrough;
234362306a36Sopenharmony_ci	case -NFS4ERR_CLID_INUSE:
234462306a36Sopenharmony_ci	case -NFS4ERR_WRONGSEC:
234562306a36Sopenharmony_ci		/* No point in retrying if we already used RPC_AUTH_UNIX */
234662306a36Sopenharmony_ci		if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX) {
234762306a36Sopenharmony_ci			status = -EPERM;
234862306a36Sopenharmony_ci			break;
234962306a36Sopenharmony_ci		}
235062306a36Sopenharmony_ci		clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX);
235162306a36Sopenharmony_ci		if (IS_ERR(clnt)) {
235262306a36Sopenharmony_ci			status = PTR_ERR(clnt);
235362306a36Sopenharmony_ci			break;
235462306a36Sopenharmony_ci		}
235562306a36Sopenharmony_ci		/* Note: this is safe because we haven't yet marked the
235662306a36Sopenharmony_ci		 * client as ready, so we are the only user of
235762306a36Sopenharmony_ci		 * clp->cl_rpcclient
235862306a36Sopenharmony_ci		 */
235962306a36Sopenharmony_ci		clnt = xchg(&clp->cl_rpcclient, clnt);
236062306a36Sopenharmony_ci		rpc_shutdown_client(clnt);
236162306a36Sopenharmony_ci		clnt = clp->cl_rpcclient;
236262306a36Sopenharmony_ci		goto again;
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	case -NFS4ERR_MINOR_VERS_MISMATCH:
236562306a36Sopenharmony_ci		status = -EPROTONOSUPPORT;
236662306a36Sopenharmony_ci		break;
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	case -EKEYEXPIRED:
236962306a36Sopenharmony_ci	case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
237062306a36Sopenharmony_ci				 * in nfs4_exchange_id */
237162306a36Sopenharmony_ci		status = -EKEYEXPIRED;
237262306a36Sopenharmony_ci		break;
237362306a36Sopenharmony_ci	default:
237462306a36Sopenharmony_ci		pr_warn("NFS: %s unhandled error %d. Exiting with error EIO\n",
237562306a36Sopenharmony_ci				__func__, status);
237662306a36Sopenharmony_ci		status = -EIO;
237762306a36Sopenharmony_ci	}
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ciout_unlock:
238062306a36Sopenharmony_ci	mutex_unlock(&nfs_clid_init_mutex);
238162306a36Sopenharmony_ci	dprintk("NFS: %s: status = %d\n", __func__, status);
238262306a36Sopenharmony_ci	return status;
238362306a36Sopenharmony_ci}
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_1
238662306a36Sopenharmony_civoid nfs4_schedule_session_recovery(struct nfs4_session *session, int err)
238762306a36Sopenharmony_ci{
238862306a36Sopenharmony_ci	struct nfs_client *clp = session->clp;
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	switch (err) {
239162306a36Sopenharmony_ci	default:
239262306a36Sopenharmony_ci		set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
239362306a36Sopenharmony_ci		break;
239462306a36Sopenharmony_ci	case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
239562306a36Sopenharmony_ci		set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
239662306a36Sopenharmony_ci	}
239762306a36Sopenharmony_ci	nfs4_schedule_state_manager(clp);
239862306a36Sopenharmony_ci}
239962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery);
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_civoid nfs41_notify_server(struct nfs_client *clp)
240262306a36Sopenharmony_ci{
240362306a36Sopenharmony_ci	/* Use CHECK_LEASE to ping the server with a SEQUENCE */
240462306a36Sopenharmony_ci	set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
240562306a36Sopenharmony_ci	nfs4_schedule_state_manager(clp);
240662306a36Sopenharmony_ci}
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_cistatic void nfs4_reset_all_state(struct nfs_client *clp)
240962306a36Sopenharmony_ci{
241062306a36Sopenharmony_ci	if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
241162306a36Sopenharmony_ci		set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
241262306a36Sopenharmony_ci		clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
241362306a36Sopenharmony_ci		nfs4_state_start_reclaim_nograce(clp);
241462306a36Sopenharmony_ci		dprintk("%s: scheduling reset of all state for server %s!\n",
241562306a36Sopenharmony_ci				__func__, clp->cl_hostname);
241662306a36Sopenharmony_ci		nfs4_schedule_state_manager(clp);
241762306a36Sopenharmony_ci	}
241862306a36Sopenharmony_ci}
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_cistatic void nfs41_handle_server_reboot(struct nfs_client *clp)
242162306a36Sopenharmony_ci{
242262306a36Sopenharmony_ci	if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
242362306a36Sopenharmony_ci		nfs4_state_start_reclaim_reboot(clp);
242462306a36Sopenharmony_ci		dprintk("%s: server %s rebooted!\n", __func__,
242562306a36Sopenharmony_ci				clp->cl_hostname);
242662306a36Sopenharmony_ci		nfs4_schedule_state_manager(clp);
242762306a36Sopenharmony_ci	}
242862306a36Sopenharmony_ci}
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_cistatic void nfs41_handle_all_state_revoked(struct nfs_client *clp)
243162306a36Sopenharmony_ci{
243262306a36Sopenharmony_ci	nfs4_reset_all_state(clp);
243362306a36Sopenharmony_ci	dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname);
243462306a36Sopenharmony_ci}
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_cistatic void nfs41_handle_some_state_revoked(struct nfs_client *clp)
243762306a36Sopenharmony_ci{
243862306a36Sopenharmony_ci	nfs4_state_start_reclaim_nograce(clp);
243962306a36Sopenharmony_ci	nfs4_schedule_state_manager(clp);
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname);
244262306a36Sopenharmony_ci}
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_cistatic void nfs41_handle_recallable_state_revoked(struct nfs_client *clp)
244562306a36Sopenharmony_ci{
244662306a36Sopenharmony_ci	/* FIXME: For now, we destroy all layouts. */
244762306a36Sopenharmony_ci	pnfs_destroy_all_layouts(clp);
244862306a36Sopenharmony_ci	nfs_test_expired_all_delegations(clp);
244962306a36Sopenharmony_ci	dprintk("%s: Recallable state revoked on server %s!\n", __func__,
245062306a36Sopenharmony_ci			clp->cl_hostname);
245162306a36Sopenharmony_ci}
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_cistatic void nfs41_handle_backchannel_fault(struct nfs_client *clp)
245462306a36Sopenharmony_ci{
245562306a36Sopenharmony_ci	set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
245662306a36Sopenharmony_ci	nfs4_schedule_state_manager(clp);
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	dprintk("%s: server %s declared a backchannel fault\n", __func__,
245962306a36Sopenharmony_ci			clp->cl_hostname);
246062306a36Sopenharmony_ci}
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_cistatic void nfs41_handle_cb_path_down(struct nfs_client *clp)
246362306a36Sopenharmony_ci{
246462306a36Sopenharmony_ci	if (test_and_set_bit(NFS4CLNT_BIND_CONN_TO_SESSION,
246562306a36Sopenharmony_ci		&clp->cl_state) == 0)
246662306a36Sopenharmony_ci		nfs4_schedule_state_manager(clp);
246762306a36Sopenharmony_ci}
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_civoid nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags,
247062306a36Sopenharmony_ci		bool recovery)
247162306a36Sopenharmony_ci{
247262306a36Sopenharmony_ci	if (!flags)
247362306a36Sopenharmony_ci		return;
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	dprintk("%s: \"%s\" (client ID %llx) flags=0x%08x\n",
247662306a36Sopenharmony_ci		__func__, clp->cl_hostname, clp->cl_clientid, flags);
247762306a36Sopenharmony_ci	/*
247862306a36Sopenharmony_ci	 * If we're called from the state manager thread, then assume we're
247962306a36Sopenharmony_ci	 * already handling the RECLAIM_NEEDED and/or STATE_REVOKED.
248062306a36Sopenharmony_ci	 * Those flags are expected to remain set until we're done
248162306a36Sopenharmony_ci	 * recovering (see RFC5661, section 18.46.3).
248262306a36Sopenharmony_ci	 */
248362306a36Sopenharmony_ci	if (recovery)
248462306a36Sopenharmony_ci		goto out_recovery;
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci	if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
248762306a36Sopenharmony_ci		nfs41_handle_server_reboot(clp);
248862306a36Sopenharmony_ci	if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED))
248962306a36Sopenharmony_ci		nfs41_handle_all_state_revoked(clp);
249062306a36Sopenharmony_ci	if (flags & (SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED |
249162306a36Sopenharmony_ci			    SEQ4_STATUS_ADMIN_STATE_REVOKED))
249262306a36Sopenharmony_ci		nfs41_handle_some_state_revoked(clp);
249362306a36Sopenharmony_ci	if (flags & SEQ4_STATUS_LEASE_MOVED)
249462306a36Sopenharmony_ci		nfs4_schedule_lease_moved_recovery(clp);
249562306a36Sopenharmony_ci	if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
249662306a36Sopenharmony_ci		nfs41_handle_recallable_state_revoked(clp);
249762306a36Sopenharmony_ciout_recovery:
249862306a36Sopenharmony_ci	if (flags & SEQ4_STATUS_BACKCHANNEL_FAULT)
249962306a36Sopenharmony_ci		nfs41_handle_backchannel_fault(clp);
250062306a36Sopenharmony_ci	else if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
250162306a36Sopenharmony_ci				SEQ4_STATUS_CB_PATH_DOWN_SESSION))
250262306a36Sopenharmony_ci		nfs41_handle_cb_path_down(clp);
250362306a36Sopenharmony_ci}
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_cistatic int nfs4_reset_session(struct nfs_client *clp)
250662306a36Sopenharmony_ci{
250762306a36Sopenharmony_ci	const struct cred *cred;
250862306a36Sopenharmony_ci	int status;
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	if (!nfs4_has_session(clp))
251162306a36Sopenharmony_ci		return 0;
251262306a36Sopenharmony_ci	status = nfs4_begin_drain_session(clp);
251362306a36Sopenharmony_ci	if (status != 0)
251462306a36Sopenharmony_ci		return status;
251562306a36Sopenharmony_ci	cred = nfs4_get_clid_cred(clp);
251662306a36Sopenharmony_ci	status = nfs4_proc_destroy_session(clp->cl_session, cred);
251762306a36Sopenharmony_ci	switch (status) {
251862306a36Sopenharmony_ci	case 0:
251962306a36Sopenharmony_ci	case -NFS4ERR_BADSESSION:
252062306a36Sopenharmony_ci	case -NFS4ERR_DEADSESSION:
252162306a36Sopenharmony_ci		break;
252262306a36Sopenharmony_ci	case -NFS4ERR_BACK_CHAN_BUSY:
252362306a36Sopenharmony_ci	case -NFS4ERR_DELAY:
252462306a36Sopenharmony_ci		set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
252562306a36Sopenharmony_ci		status = 0;
252662306a36Sopenharmony_ci		ssleep(1);
252762306a36Sopenharmony_ci		goto out;
252862306a36Sopenharmony_ci	default:
252962306a36Sopenharmony_ci		status = nfs4_recovery_handle_error(clp, status);
253062306a36Sopenharmony_ci		goto out;
253162306a36Sopenharmony_ci	}
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_ci	memset(clp->cl_session->sess_id.data, 0, NFS4_MAX_SESSIONID_LEN);
253462306a36Sopenharmony_ci	status = nfs4_proc_create_session(clp, cred);
253562306a36Sopenharmony_ci	if (status) {
253662306a36Sopenharmony_ci		dprintk("%s: session reset failed with status %d for server %s!\n",
253762306a36Sopenharmony_ci			__func__, status, clp->cl_hostname);
253862306a36Sopenharmony_ci		status = nfs4_handle_reclaim_lease_error(clp, status);
253962306a36Sopenharmony_ci		goto out;
254062306a36Sopenharmony_ci	}
254162306a36Sopenharmony_ci	nfs41_finish_session_reset(clp);
254262306a36Sopenharmony_ci	dprintk("%s: session reset was successful for server %s!\n",
254362306a36Sopenharmony_ci			__func__, clp->cl_hostname);
254462306a36Sopenharmony_ciout:
254562306a36Sopenharmony_ci	put_cred(cred);
254662306a36Sopenharmony_ci	return status;
254762306a36Sopenharmony_ci}
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_cistatic int nfs4_bind_conn_to_session(struct nfs_client *clp)
255062306a36Sopenharmony_ci{
255162306a36Sopenharmony_ci	const struct cred *cred;
255262306a36Sopenharmony_ci	int ret;
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	if (!nfs4_has_session(clp))
255562306a36Sopenharmony_ci		return 0;
255662306a36Sopenharmony_ci	ret = nfs4_begin_drain_session(clp);
255762306a36Sopenharmony_ci	if (ret != 0)
255862306a36Sopenharmony_ci		return ret;
255962306a36Sopenharmony_ci	cred = nfs4_get_clid_cred(clp);
256062306a36Sopenharmony_ci	ret = nfs4_proc_bind_conn_to_session(clp, cred);
256162306a36Sopenharmony_ci	put_cred(cred);
256262306a36Sopenharmony_ci	clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
256362306a36Sopenharmony_ci	switch (ret) {
256462306a36Sopenharmony_ci	case 0:
256562306a36Sopenharmony_ci		dprintk("%s: bind_conn_to_session was successful for server %s!\n",
256662306a36Sopenharmony_ci			__func__, clp->cl_hostname);
256762306a36Sopenharmony_ci		break;
256862306a36Sopenharmony_ci	case -NFS4ERR_DELAY:
256962306a36Sopenharmony_ci		ssleep(1);
257062306a36Sopenharmony_ci		set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
257162306a36Sopenharmony_ci		break;
257262306a36Sopenharmony_ci	default:
257362306a36Sopenharmony_ci		return nfs4_recovery_handle_error(clp, ret);
257462306a36Sopenharmony_ci	}
257562306a36Sopenharmony_ci	return 0;
257662306a36Sopenharmony_ci}
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_cistatic void nfs4_layoutreturn_any_run(struct nfs_client *clp)
257962306a36Sopenharmony_ci{
258062306a36Sopenharmony_ci	int iomode = 0;
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	if (test_and_clear_bit(NFS4CLNT_RECALL_ANY_LAYOUT_READ, &clp->cl_state))
258362306a36Sopenharmony_ci		iomode += IOMODE_READ;
258462306a36Sopenharmony_ci	if (test_and_clear_bit(NFS4CLNT_RECALL_ANY_LAYOUT_RW, &clp->cl_state))
258562306a36Sopenharmony_ci		iomode += IOMODE_RW;
258662306a36Sopenharmony_ci	/* Note: IOMODE_READ + IOMODE_RW == IOMODE_ANY */
258762306a36Sopenharmony_ci	if (iomode) {
258862306a36Sopenharmony_ci		pnfs_layout_return_unused_byclid(clp, iomode);
258962306a36Sopenharmony_ci		set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
259062306a36Sopenharmony_ci	}
259162306a36Sopenharmony_ci}
259262306a36Sopenharmony_ci#else /* CONFIG_NFS_V4_1 */
259362306a36Sopenharmony_cistatic int nfs4_reset_session(struct nfs_client *clp) { return 0; }
259462306a36Sopenharmony_ci
259562306a36Sopenharmony_cistatic int nfs4_bind_conn_to_session(struct nfs_client *clp)
259662306a36Sopenharmony_ci{
259762306a36Sopenharmony_ci	return 0;
259862306a36Sopenharmony_ci}
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_cistatic void nfs4_layoutreturn_any_run(struct nfs_client *clp)
260162306a36Sopenharmony_ci{
260262306a36Sopenharmony_ci}
260362306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_cistatic void nfs4_state_manager(struct nfs_client *clp)
260662306a36Sopenharmony_ci{
260762306a36Sopenharmony_ci	unsigned int memflags;
260862306a36Sopenharmony_ci	int status = 0;
260962306a36Sopenharmony_ci	const char *section = "", *section_sep = "";
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	/*
261262306a36Sopenharmony_ci	 * State recovery can deadlock if the direct reclaim code tries
261362306a36Sopenharmony_ci	 * start NFS writeback. So ensure memory allocations are all
261462306a36Sopenharmony_ci	 * GFP_NOFS.
261562306a36Sopenharmony_ci	 */
261662306a36Sopenharmony_ci	memflags = memalloc_nofs_save();
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	/* Ensure exclusive access to NFSv4 state */
261962306a36Sopenharmony_ci	do {
262062306a36Sopenharmony_ci		trace_nfs4_state_mgr(clp);
262162306a36Sopenharmony_ci		clear_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
262262306a36Sopenharmony_ci		if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
262362306a36Sopenharmony_ci			section = "purge state";
262462306a36Sopenharmony_ci			status = nfs4_purge_lease(clp);
262562306a36Sopenharmony_ci			if (status < 0)
262662306a36Sopenharmony_ci				goto out_error;
262762306a36Sopenharmony_ci			continue;
262862306a36Sopenharmony_ci		}
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci		if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
263162306a36Sopenharmony_ci			section = "lease expired";
263262306a36Sopenharmony_ci			/* We're going to have to re-establish a clientid */
263362306a36Sopenharmony_ci			status = nfs4_reclaim_lease(clp);
263462306a36Sopenharmony_ci			if (status < 0)
263562306a36Sopenharmony_ci				goto out_error;
263662306a36Sopenharmony_ci			continue;
263762306a36Sopenharmony_ci		}
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci		/* Initialize or reset the session */
264062306a36Sopenharmony_ci		if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) {
264162306a36Sopenharmony_ci			section = "reset session";
264262306a36Sopenharmony_ci			status = nfs4_reset_session(clp);
264362306a36Sopenharmony_ci			if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
264462306a36Sopenharmony_ci				continue;
264562306a36Sopenharmony_ci			if (status < 0)
264662306a36Sopenharmony_ci				goto out_error;
264762306a36Sopenharmony_ci		}
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci		/* Send BIND_CONN_TO_SESSION */
265062306a36Sopenharmony_ci		if (test_and_clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION,
265162306a36Sopenharmony_ci				&clp->cl_state)) {
265262306a36Sopenharmony_ci			section = "bind conn to session";
265362306a36Sopenharmony_ci			status = nfs4_bind_conn_to_session(clp);
265462306a36Sopenharmony_ci			if (status < 0)
265562306a36Sopenharmony_ci				goto out_error;
265662306a36Sopenharmony_ci			continue;
265762306a36Sopenharmony_ci		}
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_ci		if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
266062306a36Sopenharmony_ci			section = "check lease";
266162306a36Sopenharmony_ci			status = nfs4_check_lease(clp);
266262306a36Sopenharmony_ci			if (status < 0)
266362306a36Sopenharmony_ci				goto out_error;
266462306a36Sopenharmony_ci			continue;
266562306a36Sopenharmony_ci		}
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci		if (test_and_clear_bit(NFS4CLNT_MOVED, &clp->cl_state)) {
266862306a36Sopenharmony_ci			section = "migration";
266962306a36Sopenharmony_ci			status = nfs4_handle_migration(clp);
267062306a36Sopenharmony_ci			if (status < 0)
267162306a36Sopenharmony_ci				goto out_error;
267262306a36Sopenharmony_ci		}
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci		if (test_and_clear_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state)) {
267562306a36Sopenharmony_ci			section = "lease moved";
267662306a36Sopenharmony_ci			status = nfs4_handle_lease_moved(clp);
267762306a36Sopenharmony_ci			if (status < 0)
267862306a36Sopenharmony_ci				goto out_error;
267962306a36Sopenharmony_ci		}
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci		/* First recover reboot state... */
268262306a36Sopenharmony_ci		if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
268362306a36Sopenharmony_ci			section = "reclaim reboot";
268462306a36Sopenharmony_ci			status = nfs4_do_reclaim(clp,
268562306a36Sopenharmony_ci				clp->cl_mvops->reboot_recovery_ops);
268662306a36Sopenharmony_ci			if (status == -EAGAIN)
268762306a36Sopenharmony_ci				continue;
268862306a36Sopenharmony_ci			if (status < 0)
268962306a36Sopenharmony_ci				goto out_error;
269062306a36Sopenharmony_ci			nfs4_state_end_reclaim_reboot(clp);
269162306a36Sopenharmony_ci			continue;
269262306a36Sopenharmony_ci		}
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci		/* Detect expired delegations... */
269562306a36Sopenharmony_ci		if (test_and_clear_bit(NFS4CLNT_DELEGATION_EXPIRED, &clp->cl_state)) {
269662306a36Sopenharmony_ci			section = "detect expired delegations";
269762306a36Sopenharmony_ci			nfs_reap_expired_delegations(clp);
269862306a36Sopenharmony_ci			continue;
269962306a36Sopenharmony_ci		}
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci		/* Now recover expired state... */
270262306a36Sopenharmony_ci		if (test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
270362306a36Sopenharmony_ci			section = "reclaim nograce";
270462306a36Sopenharmony_ci			status = nfs4_do_reclaim(clp,
270562306a36Sopenharmony_ci				clp->cl_mvops->nograce_recovery_ops);
270662306a36Sopenharmony_ci			if (status == -EAGAIN)
270762306a36Sopenharmony_ci				continue;
270862306a36Sopenharmony_ci			if (status < 0)
270962306a36Sopenharmony_ci				goto out_error;
271062306a36Sopenharmony_ci			clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
271162306a36Sopenharmony_ci		}
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci		memalloc_nofs_restore(memflags);
271462306a36Sopenharmony_ci		nfs4_end_drain_session(clp);
271562306a36Sopenharmony_ci		nfs4_clear_state_manager_bit(clp);
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_ci		if (test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) &&
271862306a36Sopenharmony_ci		    !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING,
271962306a36Sopenharmony_ci				      &clp->cl_state)) {
272062306a36Sopenharmony_ci			memflags = memalloc_nofs_save();
272162306a36Sopenharmony_ci			continue;
272262306a36Sopenharmony_ci		}
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci		if (!test_and_set_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state)) {
272562306a36Sopenharmony_ci			if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
272662306a36Sopenharmony_ci				nfs_client_return_marked_delegations(clp);
272762306a36Sopenharmony_ci				set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
272862306a36Sopenharmony_ci			}
272962306a36Sopenharmony_ci			nfs4_layoutreturn_any_run(clp);
273062306a36Sopenharmony_ci			clear_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state);
273162306a36Sopenharmony_ci		}
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_ci		return;
273462306a36Sopenharmony_ci
273562306a36Sopenharmony_ci	} while (refcount_read(&clp->cl_count) > 1 && !signalled());
273662306a36Sopenharmony_ci	goto out_drain;
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ciout_error:
273962306a36Sopenharmony_ci	if (strlen(section))
274062306a36Sopenharmony_ci		section_sep = ": ";
274162306a36Sopenharmony_ci	trace_nfs4_state_mgr_failed(clp, section, status);
274262306a36Sopenharmony_ci	pr_warn_ratelimited("NFS: state manager%s%s failed on NFSv4 server %s"
274362306a36Sopenharmony_ci			" with error %d\n", section_sep, section,
274462306a36Sopenharmony_ci			clp->cl_hostname, -status);
274562306a36Sopenharmony_ci	ssleep(1);
274662306a36Sopenharmony_ciout_drain:
274762306a36Sopenharmony_ci	memalloc_nofs_restore(memflags);
274862306a36Sopenharmony_ci	nfs4_end_drain_session(clp);
274962306a36Sopenharmony_ci	nfs4_clear_state_manager_bit(clp);
275062306a36Sopenharmony_ci}
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_cistatic int nfs4_run_state_manager(void *ptr)
275362306a36Sopenharmony_ci{
275462306a36Sopenharmony_ci	struct nfs_client *clp = ptr;
275562306a36Sopenharmony_ci	struct rpc_clnt *cl = clp->cl_rpcclient;
275662306a36Sopenharmony_ci
275762306a36Sopenharmony_ci	while (cl != cl->cl_parent)
275862306a36Sopenharmony_ci		cl = cl->cl_parent;
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	allow_signal(SIGKILL);
276162306a36Sopenharmony_ciagain:
276262306a36Sopenharmony_ci	nfs4_state_manager(clp);
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci	if (test_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) &&
276562306a36Sopenharmony_ci	    !test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) {
276662306a36Sopenharmony_ci		wait_var_event_interruptible(&clp->cl_state,
276762306a36Sopenharmony_ci					     test_bit(NFS4CLNT_RUN_MANAGER,
276862306a36Sopenharmony_ci						      &clp->cl_state));
276962306a36Sopenharmony_ci		if (!atomic_read(&cl->cl_swapper))
277062306a36Sopenharmony_ci			clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
277162306a36Sopenharmony_ci		if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
277262306a36Sopenharmony_ci		    !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state))
277362306a36Sopenharmony_ci			goto again;
277462306a36Sopenharmony_ci		/* Either no longer a swapper, or were signalled */
277562306a36Sopenharmony_ci		clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
277662306a36Sopenharmony_ci	}
277762306a36Sopenharmony_ci
277862306a36Sopenharmony_ci	if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
277962306a36Sopenharmony_ci	    test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) &&
278062306a36Sopenharmony_ci	    !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state))
278162306a36Sopenharmony_ci		goto again;
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ci	nfs_put_client(clp);
278462306a36Sopenharmony_ci	module_put_and_kthread_exit(0);
278562306a36Sopenharmony_ci	return 0;
278662306a36Sopenharmony_ci}
2787