xref: /openbmc/linux/fs/nfs/nfs4state.c (revision 7b7fd0ac7dc1ffcaf24d9bca0f051b0168e43cd4)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  fs/nfs/nfs4state.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Client-side XDR for NFSv4.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *  Copyright (c) 2002 The Regents of the University of Michigan.
71da177e4SLinus Torvalds  *  All rights reserved.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  Kendrick Smith <kmsmith@umich.edu>
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *  Redistribution and use in source and binary forms, with or without
121da177e4SLinus Torvalds  *  modification, are permitted provided that the following conditions
131da177e4SLinus Torvalds  *  are met:
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  *  1. Redistributions of source code must retain the above copyright
161da177e4SLinus Torvalds  *     notice, this list of conditions and the following disclaimer.
171da177e4SLinus Torvalds  *  2. Redistributions in binary form must reproduce the above copyright
181da177e4SLinus Torvalds  *     notice, this list of conditions and the following disclaimer in the
191da177e4SLinus Torvalds  *     documentation and/or other materials provided with the distribution.
201da177e4SLinus Torvalds  *  3. Neither the name of the University nor the names of its
211da177e4SLinus Torvalds  *     contributors may be used to endorse or promote products derived
221da177e4SLinus Torvalds  *     from this software without specific prior written permission.
231da177e4SLinus Torvalds  *
241da177e4SLinus Torvalds  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
251da177e4SLinus Torvalds  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
261da177e4SLinus Torvalds  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
271da177e4SLinus Torvalds  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281da177e4SLinus Torvalds  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
291da177e4SLinus Torvalds  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
301da177e4SLinus Torvalds  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
311da177e4SLinus Torvalds  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
321da177e4SLinus Torvalds  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
331da177e4SLinus Torvalds  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
341da177e4SLinus Torvalds  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
351da177e4SLinus Torvalds  *
361da177e4SLinus Torvalds  * Implementation of the NFSv4 state model.  For the time being,
371da177e4SLinus Torvalds  * this is minimal, but will be made much more complex in a
381da177e4SLinus Torvalds  * subsequent patch.
391da177e4SLinus Torvalds  */
401da177e4SLinus Torvalds 
416f43ddccSTrond Myklebust #include <linux/kernel.h>
421da177e4SLinus Torvalds #include <linux/slab.h>
43b89f4321SArnd Bergmann #include <linux/fs.h>
441da177e4SLinus Torvalds #include <linux/nfs_fs.h>
455043e900STrond Myklebust #include <linux/kthread.h>
465043e900STrond Myklebust #include <linux/module.h>
479f958ab8STrond Myklebust #include <linux/random.h>
488c7597f6SRandy Dunlap #include <linux/ratelimit.h>
491da177e4SLinus Torvalds #include <linux/workqueue.h>
501da177e4SLinus Torvalds #include <linux/bitops.h>
510aaaf5c4SChuck Lever #include <linux/jiffies.h>
523e17898aSTrond Myklebust #include <linux/sched/mm.h>
531da177e4SLinus Torvalds 
5405f4c350SChuck Lever #include <linux/sunrpc/clnt.h>
5505f4c350SChuck Lever 
564ce79717STrond Myklebust #include "nfs4_fs.h"
571da177e4SLinus Torvalds #include "callback.h"
581da177e4SLinus Torvalds #include "delegation.h"
5924c8dbbbSDavid Howells #include "internal.h"
6040c64c26SAnna Schumaker #include "nfs4idmap.h"
6176e697baSTrond Myklebust #include "nfs4session.h"
62974cec8cSAndy Adamson #include "pnfs.h"
63bbe0a3aaSStanislav Kinsbursky #include "netns.h"
64511ba52eSChuck Lever #include "nfs4trace.h"
651da177e4SLinus Torvalds 
66e3c0fb7eSChuck Lever #define NFSDBG_FACILITY		NFSDBG_STATE
67e3c0fb7eSChuck Lever 
681da177e4SLinus Torvalds #define OPENOWNER_POOL_SIZE	8
691da177e4SLinus Torvalds 
7040882debSTrond Myklebust static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp);
7140882debSTrond Myklebust 
7293b717fdSTrond Myklebust const nfs4_stateid zero_stateid = {
73e0714ec4SLinus Torvalds 	{ .data = { 0 } },
7493b717fdSTrond Myklebust 	.type = NFS4_SPECIAL_STATEID_TYPE,
7593b717fdSTrond Myklebust };
76fcd8843cSTrond Myklebust const nfs4_stateid invalid_stateid = {
77fcd8843cSTrond Myklebust 	{
78445f288dSTrond Myklebust 		/* Funky initialiser keeps older gcc versions happy */
79445f288dSTrond Myklebust 		.data = { 0xff, 0xff, 0xff, 0xff, 0 },
80fcd8843cSTrond Myklebust 	},
81fcd8843cSTrond Myklebust 	.type = NFS4_INVALID_STATEID_TYPE,
82fcd8843cSTrond Myklebust };
83fcd8843cSTrond Myklebust 
842409a976SFred Isaman const nfs4_stateid current_stateid = {
852409a976SFred Isaman 	{
862409a976SFred Isaman 		/* Funky initialiser keeps older gcc versions happy */
872409a976SFred Isaman 		.data = { 0x0, 0x0, 0x0, 0x1, 0 },
882409a976SFred Isaman 	},
892409a976SFred Isaman 	.type = NFS4_SPECIAL_STATEID_TYPE,
902409a976SFred Isaman };
912409a976SFred Isaman 
9205f4c350SChuck Lever static DEFINE_MUTEX(nfs_clid_init_mutex);
931da177e4SLinus Torvalds 
nfs4_setup_state_renewal(struct nfs_client * clp)945b596830SDonald Buczek static int nfs4_setup_state_renewal(struct nfs_client *clp)
955b596830SDonald Buczek {
965b596830SDonald Buczek 	int status;
975b596830SDonald Buczek 	struct nfs_fsinfo fsinfo;
985b596830SDonald Buczek 
995b596830SDonald Buczek 	if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) {
1005b596830SDonald Buczek 		nfs4_schedule_state_renewal(clp);
1015b596830SDonald Buczek 		return 0;
1025b596830SDonald Buczek 	}
1035b596830SDonald Buczek 
1045b596830SDonald Buczek 	status = nfs4_proc_get_lease_time(clp, &fsinfo);
1055b596830SDonald Buczek 	if (status == 0) {
1067dc2993aSRobert Milkowski 		nfs4_set_lease_period(clp, fsinfo.lease_time * HZ);
1075b596830SDonald Buczek 		nfs4_schedule_state_renewal(clp);
1085b596830SDonald Buczek 	}
1095b596830SDonald Buczek 
1105b596830SDonald Buczek 	return status;
1115b596830SDonald Buczek }
1125b596830SDonald Buczek 
nfs4_init_clientid(struct nfs_client * clp,const struct cred * cred)113a52458b4SNeilBrown int nfs4_init_clientid(struct nfs_client *clp, const struct cred *cred)
1141da177e4SLinus Torvalds {
115fd954ae1STrond Myklebust 	struct nfs4_setclientid_res clid = {
116fd954ae1STrond Myklebust 		.clientid = clp->cl_clientid,
117fd954ae1STrond Myklebust 		.confirm = clp->cl_confirm,
118fd954ae1STrond Myklebust 	};
119f738f517SChuck Lever 	unsigned short port;
120f738f517SChuck Lever 	int status;
121bbe0a3aaSStanislav Kinsbursky 	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
122f738f517SChuck Lever 
123fd954ae1STrond Myklebust 	if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
124fd954ae1STrond Myklebust 		goto do_confirm;
125bbe0a3aaSStanislav Kinsbursky 	port = nn->nfs_callback_tcpport;
126f738f517SChuck Lever 	if (clp->cl_addr.ss_family == AF_INET6)
12729dcc16aSStanislav Kinsbursky 		port = nn->nfs_callback_tcpport6;
128f738f517SChuck Lever 
129bb8b27e5STrond Myklebust 	status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
130bb8b27e5STrond Myklebust 	if (status != 0)
131bb8b27e5STrond Myklebust 		goto out;
132fd954ae1STrond Myklebust 	clp->cl_clientid = clid.clientid;
133fd954ae1STrond Myklebust 	clp->cl_confirm = clid.confirm;
134fd954ae1STrond Myklebust 	set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
135fd954ae1STrond Myklebust do_confirm:
136bb8b27e5STrond Myklebust 	status = nfs4_proc_setclientid_confirm(clp, &clid, cred);
137bb8b27e5STrond Myklebust 	if (status != 0)
138bb8b27e5STrond Myklebust 		goto out;
139fd954ae1STrond Myklebust 	clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
1405b596830SDonald Buczek 	nfs4_setup_state_renewal(clp);
141bb8b27e5STrond Myklebust out:
1421da177e4SLinus Torvalds 	return status;
1431da177e4SLinus Torvalds }
1441da177e4SLinus Torvalds 
14505f4c350SChuck Lever /**
14605f4c350SChuck Lever  * nfs40_discover_server_trunking - Detect server IP address trunking (mv0)
14705f4c350SChuck Lever  *
14805f4c350SChuck Lever  * @clp: nfs_client under test
14905f4c350SChuck Lever  * @result: OUT: found nfs_client, or clp
15005f4c350SChuck Lever  * @cred: credential to use for trunking test
15105f4c350SChuck Lever  *
15205f4c350SChuck Lever  * Returns zero, a negative errno, or a negative NFS4ERR status.
15305f4c350SChuck Lever  * If zero is returned, an nfs_client pointer is planted in
15405f4c350SChuck Lever  * "result".
15505f4c350SChuck Lever  *
15605f4c350SChuck Lever  * Note: The returned client may not yet be marked ready.
15705f4c350SChuck Lever  */
nfs40_discover_server_trunking(struct nfs_client * clp,struct nfs_client ** result,const struct cred * cred)15805f4c350SChuck Lever int nfs40_discover_server_trunking(struct nfs_client *clp,
15905f4c350SChuck Lever 				   struct nfs_client **result,
160a52458b4SNeilBrown 				   const struct cred *cred)
16105f4c350SChuck Lever {
16205f4c350SChuck Lever 	struct nfs4_setclientid_res clid = {
16305f4c350SChuck Lever 		.clientid = clp->cl_clientid,
16405f4c350SChuck Lever 		.confirm = clp->cl_confirm,
16505f4c350SChuck Lever 	};
1669f62387dSTrond Myklebust 	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
16705f4c350SChuck Lever 	unsigned short port;
16805f4c350SChuck Lever 	int status;
16905f4c350SChuck Lever 
1709f62387dSTrond Myklebust 	port = nn->nfs_callback_tcpport;
17105f4c350SChuck Lever 	if (clp->cl_addr.ss_family == AF_INET6)
1729f62387dSTrond Myklebust 		port = nn->nfs_callback_tcpport6;
17305f4c350SChuck Lever 
17405f4c350SChuck Lever 	status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
17505f4c350SChuck Lever 	if (status != 0)
17605f4c350SChuck Lever 		goto out;
17705f4c350SChuck Lever 	clp->cl_clientid = clid.clientid;
17805f4c350SChuck Lever 	clp->cl_confirm = clid.confirm;
17905f4c350SChuck Lever 
18005f4c350SChuck Lever 	status = nfs40_walk_client_list(clp, result, cred);
181202c312dSTrond Myklebust 	if (status == 0) {
18205f4c350SChuck Lever 		/* Sustain the lease, even if it's empty.  If the clientid4
18305f4c350SChuck Lever 		 * goes stale it's of no use for trunking discovery. */
18405f4c350SChuck Lever 		nfs4_schedule_state_renewal(*result);
185f02f3755SZhangXiaoxu 
186f02f3755SZhangXiaoxu 		/* If the client state need to recover, do it. */
187f02f3755SZhangXiaoxu 		if (clp->cl_state)
188f02f3755SZhangXiaoxu 			nfs4_schedule_state_manager(clp);
18905f4c350SChuck Lever 	}
19005f4c350SChuck Lever out:
19105f4c350SChuck Lever 	return status;
19205f4c350SChuck Lever }
19305f4c350SChuck Lever 
nfs4_get_machine_cred(struct nfs_client * clp)194a52458b4SNeilBrown const struct cred *nfs4_get_machine_cred(struct nfs_client *clp)
195a2b2bb88STrond Myklebust {
196a52458b4SNeilBrown 	return get_cred(rpc_machine_cred());
197a2b2bb88STrond Myklebust }
198a2b2bb88STrond Myklebust 
nfs4_root_machine_cred(struct nfs_client * clp)199d688f7b8SChuck Lever static void nfs4_root_machine_cred(struct nfs_client *clp)
200d688f7b8SChuck Lever {
201d688f7b8SChuck Lever 
2025e16923bSNeilBrown 	/* Force root creds instead of machine */
2035e16923bSNeilBrown 	clp->cl_principal = NULL;
2045e16923bSNeilBrown 	clp->cl_rpcclient->cl_principal = NULL;
205d688f7b8SChuck Lever }
206d688f7b8SChuck Lever 
207a52458b4SNeilBrown static const struct cred *
nfs4_get_renew_cred_server_locked(struct nfs_server * server)20824d292b8SChuck Lever nfs4_get_renew_cred_server_locked(struct nfs_server *server)
209b4454fe1STrond Myklebust {
210a52458b4SNeilBrown 	const struct cred *cred = NULL;
211b4454fe1STrond Myklebust 	struct nfs4_state_owner *sp;
2129f958ab8STrond Myklebust 	struct rb_node *pos;
213b4454fe1STrond Myklebust 
21424d292b8SChuck Lever 	for (pos = rb_first(&server->state_owners);
21524d292b8SChuck Lever 	     pos != NULL;
21624d292b8SChuck Lever 	     pos = rb_next(pos)) {
21724d292b8SChuck Lever 		sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
218b4454fe1STrond Myklebust 		if (list_empty(&sp->so_states))
219b4454fe1STrond Myklebust 			continue;
220a52458b4SNeilBrown 		cred = get_cred(sp->so_cred);
221b4454fe1STrond Myklebust 		break;
222b4454fe1STrond Myklebust 	}
223b4454fe1STrond Myklebust 	return cred;
224b4454fe1STrond Myklebust }
225b4454fe1STrond Myklebust 
22624d292b8SChuck Lever /**
227f15e1e8bSNeilBrown  * nfs4_get_renew_cred - Acquire credential for a renew operation
22824d292b8SChuck Lever  * @clp: client state handle
22924d292b8SChuck Lever  *
23024d292b8SChuck Lever  * Returns an rpc_cred with reference count bumped, or NULL.
23124d292b8SChuck Lever  * Caller must hold clp->cl_lock.
23224d292b8SChuck Lever  */
nfs4_get_renew_cred(struct nfs_client * clp)233a52458b4SNeilBrown const struct cred *nfs4_get_renew_cred(struct nfs_client *clp)
23424d292b8SChuck Lever {
235a52458b4SNeilBrown 	const struct cred *cred = NULL;
23624d292b8SChuck Lever 	struct nfs_server *server;
23724d292b8SChuck Lever 
238e49a29bdSSachin Prabhu 	/* Use machine credentials if available */
239f15e1e8bSNeilBrown 	cred = nfs4_get_machine_cred(clp);
240e49a29bdSSachin Prabhu 	if (cred != NULL)
241e49a29bdSSachin Prabhu 		goto out;
242e49a29bdSSachin Prabhu 
243f15e1e8bSNeilBrown 	spin_lock(&clp->cl_lock);
24424d292b8SChuck Lever 	rcu_read_lock();
24524d292b8SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
24624d292b8SChuck Lever 		cred = nfs4_get_renew_cred_server_locked(server);
24724d292b8SChuck Lever 		if (cred != NULL)
24824d292b8SChuck Lever 			break;
24924d292b8SChuck Lever 	}
25024d292b8SChuck Lever 	rcu_read_unlock();
251f15e1e8bSNeilBrown 	spin_unlock(&clp->cl_lock);
252e49a29bdSSachin Prabhu 
253e49a29bdSSachin Prabhu out:
25424d292b8SChuck Lever 	return cred;
25524d292b8SChuck Lever }
25624d292b8SChuck Lever 
nfs4_end_drain_slot_table(struct nfs4_slot_table * tbl)25762f288a0SAndy Adamson static void nfs4_end_drain_slot_table(struct nfs4_slot_table *tbl)
2589dfdf404SRicardo Labiaga {
259774d5f14SAndy Adamson 	if (test_and_clear_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) {
260961a828dSTrond Myklebust 		spin_lock(&tbl->slot_tbl_lock);
261b75ad4cdSTrond Myklebust 		nfs41_wake_slot_table(tbl);
262961a828dSTrond Myklebust 		spin_unlock(&tbl->slot_tbl_lock);
263689cf5c1SAlexandros Batsakis 	}
2649dfdf404SRicardo Labiaga }
2659dfdf404SRicardo Labiaga 
nfs4_end_drain_session(struct nfs_client * clp)26662f288a0SAndy Adamson static void nfs4_end_drain_session(struct nfs_client *clp)
26762f288a0SAndy Adamson {
26862f288a0SAndy Adamson 	struct nfs4_session *ses = clp->cl_session;
26962f288a0SAndy Adamson 
2702cf8bca8SChuck Lever 	if (clp->cl_slot_tbl) {
2712cf8bca8SChuck Lever 		nfs4_end_drain_slot_table(clp->cl_slot_tbl);
2722cf8bca8SChuck Lever 		return;
2732cf8bca8SChuck Lever 	}
2742cf8bca8SChuck Lever 
27562f288a0SAndy Adamson 	if (ses != NULL) {
27662f288a0SAndy Adamson 		nfs4_end_drain_slot_table(&ses->bc_slot_table);
27762f288a0SAndy Adamson 		nfs4_end_drain_slot_table(&ses->fc_slot_table);
27862f288a0SAndy Adamson 	}
27962f288a0SAndy Adamson }
28062f288a0SAndy Adamson 
nfs4_drain_slot_tbl(struct nfs4_slot_table * tbl)281774d5f14SAndy Adamson static int nfs4_drain_slot_tbl(struct nfs4_slot_table *tbl)
2829dfdf404SRicardo Labiaga {
283774d5f14SAndy Adamson 	set_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state);
2849dfdf404SRicardo Labiaga 	spin_lock(&tbl->slot_tbl_lock);
285b6bf6e7dSAndy Adamson 	if (tbl->highest_used_slotid != NFS4_NO_SLOT) {
28616735d02SWolfram Sang 		reinit_completion(&tbl->complete);
2879dfdf404SRicardo Labiaga 		spin_unlock(&tbl->slot_tbl_lock);
28842acd021SAndy Adamson 		return wait_for_completion_interruptible(&tbl->complete);
2899dfdf404SRicardo Labiaga 	}
2909dfdf404SRicardo Labiaga 	spin_unlock(&tbl->slot_tbl_lock);
2919dfdf404SRicardo Labiaga 	return 0;
2929dfdf404SRicardo Labiaga }
2939dfdf404SRicardo Labiaga 
nfs4_begin_drain_session(struct nfs_client * clp)29442acd021SAndy Adamson static int nfs4_begin_drain_session(struct nfs_client *clp)
29542acd021SAndy Adamson {
29642acd021SAndy Adamson 	struct nfs4_session *ses = clp->cl_session;
2978aafd2fdSTrond Myklebust 	int ret;
29842acd021SAndy Adamson 
2992cf8bca8SChuck Lever 	if (clp->cl_slot_tbl)
3002cf8bca8SChuck Lever 		return nfs4_drain_slot_tbl(clp->cl_slot_tbl);
3012cf8bca8SChuck Lever 
30242acd021SAndy Adamson 	/* back channel */
303774d5f14SAndy Adamson 	ret = nfs4_drain_slot_tbl(&ses->bc_slot_table);
30442acd021SAndy Adamson 	if (ret)
30542acd021SAndy Adamson 		return ret;
30642acd021SAndy Adamson 	/* fore channel */
307774d5f14SAndy Adamson 	return nfs4_drain_slot_tbl(&ses->fc_slot_table);
30842acd021SAndy Adamson }
30942acd021SAndy Adamson 
310c9fdeb28SChuck Lever #if defined(CONFIG_NFS_V4_1)
311c9fdeb28SChuck Lever 
nfs41_finish_session_reset(struct nfs_client * clp)312bda197f5STrond Myklebust static void nfs41_finish_session_reset(struct nfs_client *clp)
313bda197f5STrond Myklebust {
314bda197f5STrond Myklebust 	clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
315bda197f5STrond Myklebust 	clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
316bda197f5STrond Myklebust 	/* create_session negotiated new slot table */
317bda197f5STrond Myklebust 	clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
318ea51efaaSDonald Buczek 	nfs4_setup_state_renewal(clp);
319bda197f5STrond Myklebust }
320bda197f5STrond Myklebust 
nfs41_init_clientid(struct nfs_client * clp,const struct cred * cred)321a52458b4SNeilBrown int nfs41_init_clientid(struct nfs_client *clp, const struct cred *cred)
3224d643d1dSAndy Adamson {
3234d643d1dSAndy Adamson 	int status;
3244d643d1dSAndy Adamson 
325fd954ae1STrond Myklebust 	if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
326fd954ae1STrond Myklebust 		goto do_confirm;
3274d643d1dSAndy Adamson 	status = nfs4_proc_exchange_id(clp, cred);
3289430fb6bSRicardo Labiaga 	if (status != 0)
3299430fb6bSRicardo Labiaga 		goto out;
330fd954ae1STrond Myklebust 	set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
331fd954ae1STrond Myklebust do_confirm:
332848f5bdaSTrond Myklebust 	status = nfs4_proc_create_session(clp, cred);
3339430fb6bSRicardo Labiaga 	if (status != 0)
3349430fb6bSRicardo Labiaga 		goto out;
33540882debSTrond Myklebust 	if (!(clp->cl_exchange_flags & EXCHGID4_FLAG_CONFIRMED_R))
33640882debSTrond Myklebust 		nfs4_state_start_reclaim_reboot(clp);
337bda197f5STrond Myklebust 	nfs41_finish_session_reset(clp);
3384d643d1dSAndy Adamson 	nfs_mark_client_ready(clp, NFS_CS_READY);
3399430fb6bSRicardo Labiaga out:
3404d643d1dSAndy Adamson 	return status;
3414d643d1dSAndy Adamson }
3424d643d1dSAndy Adamson 
34305f4c350SChuck Lever /**
34405f4c350SChuck Lever  * nfs41_discover_server_trunking - Detect server IP address trunking (mv1)
34505f4c350SChuck Lever  *
34605f4c350SChuck Lever  * @clp: nfs_client under test
34705f4c350SChuck Lever  * @result: OUT: found nfs_client, or clp
34805f4c350SChuck Lever  * @cred: credential to use for trunking test
34905f4c350SChuck Lever  *
35005f4c350SChuck Lever  * Returns NFS4_OK, a negative errno, or a negative NFS4ERR status.
35105f4c350SChuck Lever  * If NFS4_OK is returned, an nfs_client pointer is planted in
35205f4c350SChuck Lever  * "result".
35305f4c350SChuck Lever  *
35405f4c350SChuck Lever  * Note: The returned client may not yet be marked ready.
35505f4c350SChuck Lever  */
nfs41_discover_server_trunking(struct nfs_client * clp,struct nfs_client ** result,const struct cred * cred)35605f4c350SChuck Lever int nfs41_discover_server_trunking(struct nfs_client *clp,
35705f4c350SChuck Lever 				   struct nfs_client **result,
358a52458b4SNeilBrown 				   const struct cred *cred)
35905f4c350SChuck Lever {
36005f4c350SChuck Lever 	int status;
36105f4c350SChuck Lever 
36205f4c350SChuck Lever 	status = nfs4_proc_exchange_id(clp, cred);
36305f4c350SChuck Lever 	if (status != NFS4_OK)
36405f4c350SChuck Lever 		return status;
36505f4c350SChuck Lever 
36648d66b97STrond Myklebust 	status = nfs41_walk_client_list(clp, result, cred);
36748d66b97STrond Myklebust 	if (status < 0)
36848d66b97STrond Myklebust 		return status;
36948d66b97STrond Myklebust 	if (clp != *result)
37048d66b97STrond Myklebust 		return 0;
37148d66b97STrond Myklebust 
3728dcbec6dSChuck Lever 	/*
3738dcbec6dSChuck Lever 	 * Purge state if the client id was established in a prior
3748dcbec6dSChuck Lever 	 * instance and the client id could not have arrived on the
3758dcbec6dSChuck Lever 	 * server via Transparent State Migration.
3768dcbec6dSChuck Lever 	 */
3778dcbec6dSChuck Lever 	if (clp->cl_exchange_flags & EXCHGID4_FLAG_CONFIRMED_R) {
3788dcbec6dSChuck Lever 		if (!test_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags))
379e11259f9STrond Myklebust 			set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
380e11259f9STrond Myklebust 		else
38148d66b97STrond Myklebust 			set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
3828dcbec6dSChuck Lever 	}
38348d66b97STrond Myklebust 	nfs4_schedule_state_manager(clp);
38448d66b97STrond Myklebust 	status = nfs_wait_client_init_complete(clp);
38548d66b97STrond Myklebust 	if (status < 0)
38648d66b97STrond Myklebust 		nfs_put_client(clp);
38748d66b97STrond Myklebust 	return status;
38805f4c350SChuck Lever }
38905f4c350SChuck Lever 
390b4b82607SAndy Adamson #endif /* CONFIG_NFS_V4_1 */
391b4b82607SAndy Adamson 
39224d292b8SChuck Lever /**
39373d8bde5SChuck Lever  * nfs4_get_clid_cred - Acquire credential for a setclientid operation
39424d292b8SChuck Lever  * @clp: client state handle
39524d292b8SChuck Lever  *
396a52458b4SNeilBrown  * Returns a cred with reference count bumped, or NULL.
39724d292b8SChuck Lever  */
nfs4_get_clid_cred(struct nfs_client * clp)398a52458b4SNeilBrown const struct cred *nfs4_get_clid_cred(struct nfs_client *clp)
39924d292b8SChuck Lever {
400a52458b4SNeilBrown 	const struct cred *cred;
40124d292b8SChuck Lever 
402f15e1e8bSNeilBrown 	cred = nfs4_get_machine_cred(clp);
40324d292b8SChuck Lever 	return cred;
40424d292b8SChuck Lever }
40524d292b8SChuck Lever 
4061da177e4SLinus Torvalds static struct nfs4_state_owner *
nfs4_find_state_owner_locked(struct nfs_server * server,const struct cred * cred)407a52458b4SNeilBrown nfs4_find_state_owner_locked(struct nfs_server *server, const struct cred *cred)
4081da177e4SLinus Torvalds {
40924d292b8SChuck Lever 	struct rb_node **p = &server->state_owners.rb_node,
4109f958ab8STrond Myklebust 		       *parent = NULL;
411414adf14SChuck Lever 	struct nfs4_state_owner *sp;
412a52458b4SNeilBrown 	int cmp;
4131da177e4SLinus Torvalds 
4149f958ab8STrond Myklebust 	while (*p != NULL) {
4159f958ab8STrond Myklebust 		parent = *p;
41624d292b8SChuck Lever 		sp = rb_entry(parent, struct nfs4_state_owner, so_server_node);
417a52458b4SNeilBrown 		cmp = cred_fscmp(cred, sp->so_cred);
4189f958ab8STrond Myklebust 
419a52458b4SNeilBrown 		if (cmp < 0)
4209f958ab8STrond Myklebust 			p = &parent->rb_left;
421a52458b4SNeilBrown 		else if (cmp > 0)
4229f958ab8STrond Myklebust 			p = &parent->rb_right;
4239f958ab8STrond Myklebust 		else {
4240aaaf5c4SChuck Lever 			if (!list_empty(&sp->so_lru))
4250aaaf5c4SChuck Lever 				list_del_init(&sp->so_lru);
4261da177e4SLinus Torvalds 			atomic_inc(&sp->so_count);
427414adf14SChuck Lever 			return sp;
4281da177e4SLinus Torvalds 		}
4299f958ab8STrond Myklebust 	}
430414adf14SChuck Lever 	return NULL;
4311da177e4SLinus Torvalds }
4321da177e4SLinus Torvalds 
4339f958ab8STrond Myklebust static struct nfs4_state_owner *
nfs4_insert_state_owner_locked(struct nfs4_state_owner * new)43424d292b8SChuck Lever nfs4_insert_state_owner_locked(struct nfs4_state_owner *new)
4359f958ab8STrond Myklebust {
43624d292b8SChuck Lever 	struct nfs_server *server = new->so_server;
43724d292b8SChuck Lever 	struct rb_node **p = &server->state_owners.rb_node,
4389f958ab8STrond Myklebust 		       *parent = NULL;
4399f958ab8STrond Myklebust 	struct nfs4_state_owner *sp;
440a52458b4SNeilBrown 	int cmp;
4419f958ab8STrond Myklebust 
4429f958ab8STrond Myklebust 	while (*p != NULL) {
4439f958ab8STrond Myklebust 		parent = *p;
44424d292b8SChuck Lever 		sp = rb_entry(parent, struct nfs4_state_owner, so_server_node);
445a52458b4SNeilBrown 		cmp = cred_fscmp(new->so_cred, sp->so_cred);
4469f958ab8STrond Myklebust 
447a52458b4SNeilBrown 		if (cmp < 0)
4489f958ab8STrond Myklebust 			p = &parent->rb_left;
449a52458b4SNeilBrown 		else if (cmp > 0)
4509f958ab8STrond Myklebust 			p = &parent->rb_right;
4519f958ab8STrond Myklebust 		else {
4520aaaf5c4SChuck Lever 			if (!list_empty(&sp->so_lru))
4530aaaf5c4SChuck Lever 				list_del_init(&sp->so_lru);
4549f958ab8STrond Myklebust 			atomic_inc(&sp->so_count);
4559f958ab8STrond Myklebust 			return sp;
4569f958ab8STrond Myklebust 		}
4579f958ab8STrond Myklebust 	}
45824d292b8SChuck Lever 	rb_link_node(&new->so_server_node, parent, p);
45924d292b8SChuck Lever 	rb_insert_color(&new->so_server_node, &server->state_owners);
4609f958ab8STrond Myklebust 	return new;
4619f958ab8STrond Myklebust }
4629f958ab8STrond Myklebust 
4639f958ab8STrond Myklebust static void
nfs4_remove_state_owner_locked(struct nfs4_state_owner * sp)46424d292b8SChuck Lever nfs4_remove_state_owner_locked(struct nfs4_state_owner *sp)
4659f958ab8STrond Myklebust {
46624d292b8SChuck Lever 	struct nfs_server *server = sp->so_server;
46724d292b8SChuck Lever 
46824d292b8SChuck Lever 	if (!RB_EMPTY_NODE(&sp->so_server_node))
46924d292b8SChuck Lever 		rb_erase(&sp->so_server_node, &server->state_owners);
4709f958ab8STrond Myklebust }
4719f958ab8STrond Myklebust 
4727ba127abSTrond Myklebust static void
nfs4_init_seqid_counter(struct nfs_seqid_counter * sc)4737ba127abSTrond Myklebust nfs4_init_seqid_counter(struct nfs_seqid_counter *sc)
4747ba127abSTrond Myklebust {
47595b72eb0STrond Myklebust 	sc->create_time = ktime_get();
4767ba127abSTrond Myklebust 	sc->flags = 0;
4777ba127abSTrond Myklebust 	sc->counter = 0;
4787ba127abSTrond Myklebust 	spin_lock_init(&sc->lock);
4797ba127abSTrond Myklebust 	INIT_LIST_HEAD(&sc->list);
4807ba127abSTrond Myklebust 	rpc_init_wait_queue(&sc->wait, "Seqid_waitqueue");
4817ba127abSTrond Myklebust }
4827ba127abSTrond Myklebust 
4837ba127abSTrond Myklebust static void
nfs4_destroy_seqid_counter(struct nfs_seqid_counter * sc)4847ba127abSTrond Myklebust nfs4_destroy_seqid_counter(struct nfs_seqid_counter *sc)
4857ba127abSTrond Myklebust {
4867ba127abSTrond Myklebust 	rpc_destroy_wait_queue(&sc->wait);
4877ba127abSTrond Myklebust }
4887ba127abSTrond Myklebust 
4891da177e4SLinus Torvalds /*
4901da177e4SLinus Torvalds  * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to
4911da177e4SLinus Torvalds  * create a new state_owner.
4921da177e4SLinus Torvalds  *
4931da177e4SLinus Torvalds  */
4941da177e4SLinus Torvalds static struct nfs4_state_owner *
nfs4_alloc_state_owner(struct nfs_server * server,const struct cred * cred,gfp_t gfp_flags)495d1e284d5STrond Myklebust nfs4_alloc_state_owner(struct nfs_server *server,
496a52458b4SNeilBrown 		const struct cred *cred,
497d1e284d5STrond Myklebust 		gfp_t gfp_flags)
4981da177e4SLinus Torvalds {
4991da177e4SLinus Torvalds 	struct nfs4_state_owner *sp;
5001da177e4SLinus Torvalds 
501d1e284d5STrond Myklebust 	sp = kzalloc(sizeof(*sp), gfp_flags);
5021da177e4SLinus Torvalds 	if (!sp)
5031da177e4SLinus Torvalds 		return NULL;
504724e2df9SBo Liu 	sp->so_seqid.owner_id = ida_alloc(&server->openowner_id, gfp_flags);
505aae5730eSMatthew Wilcox 	if (sp->so_seqid.owner_id < 0) {
506aae5730eSMatthew Wilcox 		kfree(sp);
507aae5730eSMatthew Wilcox 		return NULL;
508aae5730eSMatthew Wilcox 	}
509d1e284d5STrond Myklebust 	sp->so_server = server;
510a52458b4SNeilBrown 	sp->so_cred = get_cred(cred);
511ec073428STrond Myklebust 	spin_lock_init(&sp->so_lock);
5121da177e4SLinus Torvalds 	INIT_LIST_HEAD(&sp->so_states);
5137ba127abSTrond Myklebust 	nfs4_init_seqid_counter(&sp->so_seqid);
5141da177e4SLinus Torvalds 	atomic_set(&sp->so_count, 1);
5150aaaf5c4SChuck Lever 	INIT_LIST_HEAD(&sp->so_lru);
51676246c92SAhmed S. Darwish 	seqcount_spinlock_init(&sp->so_reclaim_seqcount, &sp->so_lock);
51765b62a29STrond Myklebust 	mutex_init(&sp->so_delegreturn_mutex);
5181da177e4SLinus Torvalds 	return sp;
5191da177e4SLinus Torvalds }
5201da177e4SLinus Torvalds 
5211d2e88e7SAdrian Bunk static void
nfs4_reset_state_owner(struct nfs4_state_owner * sp)52286cfb041SNeilBrown nfs4_reset_state_owner(struct nfs4_state_owner *sp)
5231da177e4SLinus Torvalds {
52486cfb041SNeilBrown 	/* This state_owner is no longer usable, but must
52586cfb041SNeilBrown 	 * remain in place so that state recovery can find it
52686cfb041SNeilBrown 	 * and the opens associated with it.
52786cfb041SNeilBrown 	 * It may also be used for new 'open' request to
52886cfb041SNeilBrown 	 * return a delegation to the server.
52986cfb041SNeilBrown 	 * So update the 'create_time' so that it looks like
53086cfb041SNeilBrown 	 * a new state_owner.  This will cause the server to
53186cfb041SNeilBrown 	 * request an OPEN_CONFIRM to start a new sequence.
53286cfb041SNeilBrown 	 */
53386cfb041SNeilBrown 	sp->so_seqid.create_time = ktime_get();
5349f958ab8STrond Myklebust }
5351da177e4SLinus Torvalds 
nfs4_free_state_owner(struct nfs4_state_owner * sp)5360aaaf5c4SChuck Lever static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
5370aaaf5c4SChuck Lever {
5387ba127abSTrond Myklebust 	nfs4_destroy_seqid_counter(&sp->so_seqid);
539a52458b4SNeilBrown 	put_cred(sp->so_cred);
540724e2df9SBo Liu 	ida_free(&sp->so_server->openowner_id, sp->so_seqid.owner_id);
5410aaaf5c4SChuck Lever 	kfree(sp);
5420aaaf5c4SChuck Lever }
5430aaaf5c4SChuck Lever 
nfs4_gc_state_owners(struct nfs_server * server)5440aaaf5c4SChuck Lever static void nfs4_gc_state_owners(struct nfs_server *server)
5450aaaf5c4SChuck Lever {
5460aaaf5c4SChuck Lever 	struct nfs_client *clp = server->nfs_client;
5470aaaf5c4SChuck Lever 	struct nfs4_state_owner *sp, *tmp;
5480aaaf5c4SChuck Lever 	unsigned long time_min, time_max;
5490aaaf5c4SChuck Lever 	LIST_HEAD(doomed);
5500aaaf5c4SChuck Lever 
5510aaaf5c4SChuck Lever 	spin_lock(&clp->cl_lock);
5520aaaf5c4SChuck Lever 	time_max = jiffies;
5530aaaf5c4SChuck Lever 	time_min = (long)time_max - (long)clp->cl_lease_time;
5540aaaf5c4SChuck Lever 	list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) {
5550aaaf5c4SChuck Lever 		/* NB: LRU is sorted so that oldest is at the head */
5560aaaf5c4SChuck Lever 		if (time_in_range(sp->so_expires, time_min, time_max))
5570aaaf5c4SChuck Lever 			break;
5580aaaf5c4SChuck Lever 		list_move(&sp->so_lru, &doomed);
5590aaaf5c4SChuck Lever 		nfs4_remove_state_owner_locked(sp);
5600aaaf5c4SChuck Lever 	}
5610aaaf5c4SChuck Lever 	spin_unlock(&clp->cl_lock);
5620aaaf5c4SChuck Lever 
5630aaaf5c4SChuck Lever 	list_for_each_entry_safe(sp, tmp, &doomed, so_lru) {
5640aaaf5c4SChuck Lever 		list_del(&sp->so_lru);
5650aaaf5c4SChuck Lever 		nfs4_free_state_owner(sp);
5660aaaf5c4SChuck Lever 	}
5670aaaf5c4SChuck Lever }
5680aaaf5c4SChuck Lever 
56924d292b8SChuck Lever /**
57024d292b8SChuck Lever  * nfs4_get_state_owner - Look up a state owner given a credential
57124d292b8SChuck Lever  * @server: nfs_server to search
57224d292b8SChuck Lever  * @cred: RPC credential to match
573302fad7bSTrond Myklebust  * @gfp_flags: allocation mode
57424d292b8SChuck Lever  *
57524d292b8SChuck Lever  * Returns a pointer to an instantiated nfs4_state_owner struct, or NULL.
57624d292b8SChuck Lever  */
nfs4_get_state_owner(struct nfs_server * server,const struct cred * cred,gfp_t gfp_flags)57724d292b8SChuck Lever struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server,
578a52458b4SNeilBrown 					      const struct cred *cred,
579d1e284d5STrond Myklebust 					      gfp_t gfp_flags)
5801da177e4SLinus Torvalds {
5817539bbabSDavid Howells 	struct nfs_client *clp = server->nfs_client;
5821da177e4SLinus Torvalds 	struct nfs4_state_owner *sp, *new;
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 	spin_lock(&clp->cl_lock);
58524d292b8SChuck Lever 	sp = nfs4_find_state_owner_locked(server, cred);
5861da177e4SLinus Torvalds 	spin_unlock(&clp->cl_lock);
5871da177e4SLinus Torvalds 	if (sp != NULL)
5880aaaf5c4SChuck Lever 		goto out;
589d1e284d5STrond Myklebust 	new = nfs4_alloc_state_owner(server, cred, gfp_flags);
5909f958ab8STrond Myklebust 	if (new == NULL)
5910aaaf5c4SChuck Lever 		goto out;
5929157c31dSTrond Myklebust 	spin_lock(&clp->cl_lock);
59324d292b8SChuck Lever 	sp = nfs4_insert_state_owner_locked(new);
5949f958ab8STrond Myklebust 	spin_unlock(&clp->cl_lock);
595d1e284d5STrond Myklebust 	if (sp != new)
596d1e284d5STrond Myklebust 		nfs4_free_state_owner(new);
5970aaaf5c4SChuck Lever out:
5980aaaf5c4SChuck Lever 	nfs4_gc_state_owners(server);
5999f958ab8STrond Myklebust 	return sp;
6001da177e4SLinus Torvalds }
6011da177e4SLinus Torvalds 
60224d292b8SChuck Lever /**
60324d292b8SChuck Lever  * nfs4_put_state_owner - Release a nfs4_state_owner
60424d292b8SChuck Lever  * @sp: state owner data to release
6057bf97bc2STrond Myklebust  *
6067bf97bc2STrond Myklebust  * Note that we keep released state owners on an LRU
6077bf97bc2STrond Myklebust  * list.
6087bf97bc2STrond Myklebust  * This caches valid state owners so that they can be
6097bf97bc2STrond Myklebust  * reused, to avoid the OPEN_CONFIRM on minor version 0.
6107bf97bc2STrond Myklebust  * It also pins the uniquifier of dropped state owners for
6117bf97bc2STrond Myklebust  * a while, to ensure that those state owner names are
6127bf97bc2STrond Myklebust  * never reused.
61324d292b8SChuck Lever  */
nfs4_put_state_owner(struct nfs4_state_owner * sp)6141da177e4SLinus Torvalds void nfs4_put_state_owner(struct nfs4_state_owner *sp)
6151da177e4SLinus Torvalds {
6160aaaf5c4SChuck Lever 	struct nfs_server *server = sp->so_server;
6170aaaf5c4SChuck Lever 	struct nfs_client *clp = server->nfs_client;
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds 	if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock))
6201da177e4SLinus Torvalds 		return;
6210aaaf5c4SChuck Lever 
6220aaaf5c4SChuck Lever 	sp->so_expires = jiffies;
6230aaaf5c4SChuck Lever 	list_add_tail(&sp->so_lru, &server->state_owners_lru);
6240aaaf5c4SChuck Lever 	spin_unlock(&clp->cl_lock);
6250aaaf5c4SChuck Lever }
6260aaaf5c4SChuck Lever 
6270aaaf5c4SChuck Lever /**
6280aaaf5c4SChuck Lever  * nfs4_purge_state_owners - Release all cached state owners
6290aaaf5c4SChuck Lever  * @server: nfs_server with cached state owners to release
630c77e2283STrond Myklebust  * @head: resulting list of state owners
6310aaaf5c4SChuck Lever  *
6320aaaf5c4SChuck Lever  * Called at umount time.  Remaining state owners will be on
6330aaaf5c4SChuck Lever  * the LRU with ref count of zero.
634c77e2283STrond Myklebust  * Note that the state owners are not freed, but are added
635c77e2283STrond Myklebust  * to the list @head, which can later be used as an argument
636c77e2283STrond Myklebust  * to nfs4_free_state_owners.
6370aaaf5c4SChuck Lever  */
nfs4_purge_state_owners(struct nfs_server * server,struct list_head * head)638c77e2283STrond Myklebust void nfs4_purge_state_owners(struct nfs_server *server, struct list_head *head)
6390aaaf5c4SChuck Lever {
6400aaaf5c4SChuck Lever 	struct nfs_client *clp = server->nfs_client;
6410aaaf5c4SChuck Lever 	struct nfs4_state_owner *sp, *tmp;
6420aaaf5c4SChuck Lever 
6430aaaf5c4SChuck Lever 	spin_lock(&clp->cl_lock);
6440aaaf5c4SChuck Lever 	list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) {
645c77e2283STrond Myklebust 		list_move(&sp->so_lru, head);
6460aaaf5c4SChuck Lever 		nfs4_remove_state_owner_locked(sp);
6470aaaf5c4SChuck Lever 	}
6480aaaf5c4SChuck Lever 	spin_unlock(&clp->cl_lock);
649c77e2283STrond Myklebust }
6500aaaf5c4SChuck Lever 
651c77e2283STrond Myklebust /**
6526453bcd0STrond Myklebust  * nfs4_free_state_owners - Release all cached state owners
653c77e2283STrond Myklebust  * @head: resulting list of state owners
654c77e2283STrond Myklebust  *
655c77e2283STrond Myklebust  * Frees a list of state owners that was generated by
656c77e2283STrond Myklebust  * nfs4_purge_state_owners
657c77e2283STrond Myklebust  */
nfs4_free_state_owners(struct list_head * head)658c77e2283STrond Myklebust void nfs4_free_state_owners(struct list_head *head)
659c77e2283STrond Myklebust {
660c77e2283STrond Myklebust 	struct nfs4_state_owner *sp, *tmp;
661c77e2283STrond Myklebust 
662c77e2283STrond Myklebust 	list_for_each_entry_safe(sp, tmp, head, so_lru) {
6630aaaf5c4SChuck Lever 		list_del(&sp->so_lru);
6640aaaf5c4SChuck Lever 		nfs4_free_state_owner(sp);
6650aaaf5c4SChuck Lever 	}
6661da177e4SLinus Torvalds }
6671da177e4SLinus Torvalds 
6681da177e4SLinus Torvalds static struct nfs4_state *
nfs4_alloc_open_state(void)6691da177e4SLinus Torvalds nfs4_alloc_open_state(void)
6701da177e4SLinus Torvalds {
6711da177e4SLinus Torvalds 	struct nfs4_state *state;
6721da177e4SLinus Torvalds 
6739c00fd9aSTrond Myklebust 	state = kzalloc(sizeof(*state), GFP_KERNEL_ACCOUNT);
6741da177e4SLinus Torvalds 	if (!state)
6751da177e4SLinus Torvalds 		return NULL;
676ace9fad4STrond Myklebust 	refcount_set(&state->count, 1);
6771da177e4SLinus Torvalds 	INIT_LIST_HEAD(&state->lock_states);
6788d0a8a9dSTrond Myklebust 	spin_lock_init(&state->state_lock);
6798bda4e4cSTrond Myklebust 	seqlock_init(&state->seqlock);
680c9399f21STrond Myklebust 	init_waitqueue_head(&state->waitq);
6811da177e4SLinus Torvalds 	return state;
6821da177e4SLinus Torvalds }
6831da177e4SLinus Torvalds 
6844cecb76fSTrond Myklebust void
nfs4_state_set_mode_locked(struct nfs4_state * state,fmode_t fmode)685dc0b027dSTrond Myklebust nfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode)
6864cecb76fSTrond Myklebust {
687dc0b027dSTrond Myklebust 	if (state->state == fmode)
6884cecb76fSTrond Myklebust 		return;
6894cecb76fSTrond Myklebust 	/* NB! List reordering - see the reclaim code for why.  */
690dc0b027dSTrond Myklebust 	if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
691dc0b027dSTrond Myklebust 		if (fmode & FMODE_WRITE)
6924cecb76fSTrond Myklebust 			list_move(&state->open_states, &state->owner->so_states);
6934cecb76fSTrond Myklebust 		else
6944cecb76fSTrond Myklebust 			list_move_tail(&state->open_states, &state->owner->so_states);
6954cecb76fSTrond Myklebust 	}
696dc0b027dSTrond Myklebust 	state->state = fmode;
6974cecb76fSTrond Myklebust }
6984cecb76fSTrond Myklebust 
6991da177e4SLinus Torvalds static struct nfs4_state *
__nfs4_find_state_byowner(struct inode * inode,struct nfs4_state_owner * owner)7001da177e4SLinus Torvalds __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
7011da177e4SLinus Torvalds {
7021da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
7031da177e4SLinus Torvalds 	struct nfs4_state *state;
7041da177e4SLinus Torvalds 
7059ae075fdSTrond Myklebust 	list_for_each_entry_rcu(state, &nfsi->open_states, inode_states) {
7061c816efaSTrond Myklebust 		if (state->owner != owner)
7071da177e4SLinus Torvalds 			continue;
7085d422301STrond Myklebust 		if (!nfs4_valid_open_stateid(state))
7095d422301STrond Myklebust 			continue;
710ace9fad4STrond Myklebust 		if (refcount_inc_not_zero(&state->count))
7111da177e4SLinus Torvalds 			return state;
7121da177e4SLinus Torvalds 	}
7131da177e4SLinus Torvalds 	return NULL;
7141da177e4SLinus Torvalds }
7151da177e4SLinus Torvalds 
7161da177e4SLinus Torvalds static void
nfs4_free_open_state(struct nfs4_state * state)7171da177e4SLinus Torvalds nfs4_free_open_state(struct nfs4_state *state)
7181da177e4SLinus Torvalds {
7199ae075fdSTrond Myklebust 	kfree_rcu(state, rcu_head);
7201da177e4SLinus Torvalds }
7211da177e4SLinus Torvalds 
7221da177e4SLinus Torvalds struct nfs4_state *
nfs4_get_open_state(struct inode * inode,struct nfs4_state_owner * owner)7231da177e4SLinus Torvalds nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
7241da177e4SLinus Torvalds {
7251da177e4SLinus Torvalds 	struct nfs4_state *state, *new;
7261da177e4SLinus Torvalds 	struct nfs_inode *nfsi = NFS_I(inode);
7271da177e4SLinus Torvalds 
7289ae075fdSTrond Myklebust 	rcu_read_lock();
7291da177e4SLinus Torvalds 	state = __nfs4_find_state_byowner(inode, owner);
7309ae075fdSTrond Myklebust 	rcu_read_unlock();
7311da177e4SLinus Torvalds 	if (state)
7321da177e4SLinus Torvalds 		goto out;
7331da177e4SLinus Torvalds 	new = nfs4_alloc_open_state();
734ec073428STrond Myklebust 	spin_lock(&owner->so_lock);
7351da177e4SLinus Torvalds 	spin_lock(&inode->i_lock);
7361da177e4SLinus Torvalds 	state = __nfs4_find_state_byowner(inode, owner);
7371da177e4SLinus Torvalds 	if (state == NULL && new != NULL) {
7381da177e4SLinus Torvalds 		state = new;
7391da177e4SLinus Torvalds 		state->owner = owner;
7401da177e4SLinus Torvalds 		atomic_inc(&owner->so_count);
7410444d76aSDave Chinner 		ihold(inode);
7420444d76aSDave Chinner 		state->inode = inode;
74329fe8399SJ. Bruce Fields 		list_add_rcu(&state->inode_states, &nfsi->open_states);
7441da177e4SLinus Torvalds 		spin_unlock(&inode->i_lock);
745ec073428STrond Myklebust 		/* Note: The reclaim code dictates that we add stateless
746ec073428STrond Myklebust 		 * and read-only stateids to the end of the list */
747ec073428STrond Myklebust 		list_add_tail(&state->open_states, &owner->so_states);
748ec073428STrond Myklebust 		spin_unlock(&owner->so_lock);
7491da177e4SLinus Torvalds 	} else {
7501da177e4SLinus Torvalds 		spin_unlock(&inode->i_lock);
751ec073428STrond Myklebust 		spin_unlock(&owner->so_lock);
7521da177e4SLinus Torvalds 		if (new)
7531da177e4SLinus Torvalds 			nfs4_free_open_state(new);
7541da177e4SLinus Torvalds 	}
7551da177e4SLinus Torvalds out:
7561da177e4SLinus Torvalds 	return state;
7571da177e4SLinus Torvalds }
7581da177e4SLinus Torvalds 
nfs4_put_open_state(struct nfs4_state * state)7591da177e4SLinus Torvalds void nfs4_put_open_state(struct nfs4_state *state)
7601da177e4SLinus Torvalds {
7611da177e4SLinus Torvalds 	struct inode *inode = state->inode;
7621da177e4SLinus Torvalds 	struct nfs4_state_owner *owner = state->owner;
7631da177e4SLinus Torvalds 
764ace9fad4STrond Myklebust 	if (!refcount_dec_and_lock(&state->count, &owner->so_lock))
7651da177e4SLinus Torvalds 		return;
766ec073428STrond Myklebust 	spin_lock(&inode->i_lock);
7679ae075fdSTrond Myklebust 	list_del_rcu(&state->inode_states);
7681da177e4SLinus Torvalds 	list_del(&state->open_states);
769ec073428STrond Myklebust 	spin_unlock(&inode->i_lock);
770ec073428STrond Myklebust 	spin_unlock(&owner->so_lock);
771b7b7dac6STrond Myklebust 	nfs4_inode_return_delegation_on_close(inode);
7721da177e4SLinus Torvalds 	iput(inode);
7731da177e4SLinus Torvalds 	nfs4_free_open_state(state);
7741da177e4SLinus Torvalds 	nfs4_put_state_owner(owner);
7751da177e4SLinus Torvalds }
7761da177e4SLinus Torvalds 
7771da177e4SLinus Torvalds /*
77883c9d41eSTrond Myklebust  * Close the current file.
7791da177e4SLinus Torvalds  */
__nfs4_close(struct nfs4_state * state,fmode_t fmode,gfp_t gfp_mask,int wait)780643168c2SAl Viro static void __nfs4_close(struct nfs4_state *state,
7818535b2beSTrond Myklebust 		fmode_t fmode, gfp_t gfp_mask, int wait)
7821da177e4SLinus Torvalds {
7831da177e4SLinus Torvalds 	struct nfs4_state_owner *owner = state->owner;
784003707c7STrond Myklebust 	int call_close = 0;
785dc0b027dSTrond Myklebust 	fmode_t newstate;
7861da177e4SLinus Torvalds 
7871da177e4SLinus Torvalds 	atomic_inc(&owner->so_count);
7881da177e4SLinus Torvalds 	/* Protect against nfs4_find_state() */
789ec073428STrond Myklebust 	spin_lock(&owner->so_lock);
790dc0b027dSTrond Myklebust 	switch (fmode & (FMODE_READ | FMODE_WRITE)) {
791e7616923STrond Myklebust 		case FMODE_READ:
792e7616923STrond Myklebust 			state->n_rdonly--;
793e7616923STrond Myklebust 			break;
794e7616923STrond Myklebust 		case FMODE_WRITE:
795e7616923STrond Myklebust 			state->n_wronly--;
796e7616923STrond Myklebust 			break;
797e7616923STrond Myklebust 		case FMODE_READ|FMODE_WRITE:
798e7616923STrond Myklebust 			state->n_rdwr--;
799e7616923STrond Myklebust 	}
800003707c7STrond Myklebust 	newstate = FMODE_READ|FMODE_WRITE;
801e7616923STrond Myklebust 	if (state->n_rdwr == 0) {
802003707c7STrond Myklebust 		if (state->n_rdonly == 0) {
8034cecb76fSTrond Myklebust 			newstate &= ~FMODE_READ;
804003707c7STrond Myklebust 			call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags);
805003707c7STrond Myklebust 			call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
806003707c7STrond Myklebust 		}
807003707c7STrond Myklebust 		if (state->n_wronly == 0) {
8084cecb76fSTrond Myklebust 			newstate &= ~FMODE_WRITE;
809003707c7STrond Myklebust 			call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags);
810003707c7STrond Myklebust 			call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
811e7616923STrond Myklebust 		}
812003707c7STrond Myklebust 		if (newstate == 0)
813003707c7STrond Myklebust 			clear_bit(NFS_DELEGATED_STATE, &state->flags);
814003707c7STrond Myklebust 	}
8154cecb76fSTrond Myklebust 	nfs4_state_set_mode_locked(state, newstate);
816ec073428STrond Myklebust 	spin_unlock(&owner->so_lock);
8174cecb76fSTrond Myklebust 
818003707c7STrond Myklebust 	if (!call_close) {
8191da177e4SLinus Torvalds 		nfs4_put_open_state(state);
8201da177e4SLinus Torvalds 		nfs4_put_state_owner(owner);
8211f7977c1STrond Myklebust 	} else
8221f7977c1STrond Myklebust 		nfs4_do_close(state, gfp_mask, wait);
823a49c3c77STrond Myklebust }
824a49c3c77STrond Myklebust 
nfs4_close_state(struct nfs4_state * state,fmode_t fmode)825643168c2SAl Viro void nfs4_close_state(struct nfs4_state *state, fmode_t fmode)
826a49c3c77STrond Myklebust {
827da48f267STrond Myklebust 	__nfs4_close(state, fmode, GFP_KERNEL, 0);
828a49c3c77STrond Myklebust }
829a49c3c77STrond Myklebust 
nfs4_close_sync(struct nfs4_state * state,fmode_t fmode)830643168c2SAl Viro void nfs4_close_sync(struct nfs4_state *state, fmode_t fmode)
831a49c3c77STrond Myklebust {
832643168c2SAl Viro 	__nfs4_close(state, fmode, GFP_KERNEL, 1);
8331da177e4SLinus Torvalds }
8341da177e4SLinus Torvalds 
8351da177e4SLinus Torvalds /*
8361da177e4SLinus Torvalds  * Search the state->lock_states for an existing lock_owner
8373f8f2548SNeilBrown  * that is compatible with either of the given owners.
8383f8f2548SNeilBrown  * If the second is non-zero, then the first refers to a Posix-lock
8393f8f2548SNeilBrown  * owner (current->files) and the second refers to a flock/OFD
8403f8f2548SNeilBrown  * owner (struct file*).  In that case, prefer a match for the first
8413f8f2548SNeilBrown  * owner.
8423f8f2548SNeilBrown  * If both sorts of locks are held on the one file we cannot know
8433f8f2548SNeilBrown  * which stateid was intended to be used, so a "correct" choice cannot
8443f8f2548SNeilBrown  * be made.  Failing that, a "consistent" choice is preferable.  The
8453f8f2548SNeilBrown  * consistent choice we make is to prefer the first owner, that of a
8463f8f2548SNeilBrown  * Posix lock.
8471da177e4SLinus Torvalds  */
8481da177e4SLinus Torvalds static struct nfs4_lock_state *
__nfs4_find_lock_state(struct nfs4_state * state,fl_owner_t fl_owner,fl_owner_t fl_owner2)8498d424431SNeilBrown __nfs4_find_lock_state(struct nfs4_state *state,
8508d424431SNeilBrown 		       fl_owner_t fl_owner, fl_owner_t fl_owner2)
8511da177e4SLinus Torvalds {
8523f8f2548SNeilBrown 	struct nfs4_lock_state *pos, *ret = NULL;
8531da177e4SLinus Torvalds 	list_for_each_entry(pos, &state->lock_states, ls_locks) {
8543f8f2548SNeilBrown 		if (pos->ls_owner == fl_owner) {
8553f8f2548SNeilBrown 			ret = pos;
8563f8f2548SNeilBrown 			break;
8571da177e4SLinus Torvalds 		}
8583f8f2548SNeilBrown 		if (pos->ls_owner == fl_owner2)
8593f8f2548SNeilBrown 			ret = pos;
8603f8f2548SNeilBrown 	}
8613f8f2548SNeilBrown 	if (ret)
862194bc1f4SElena Reshetova 		refcount_inc(&ret->ls_count);
8633f8f2548SNeilBrown 	return ret;
8641da177e4SLinus Torvalds }
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds /*
8671da177e4SLinus Torvalds  * Return a compatible lock_state. If no initialized lock_state structure
8681da177e4SLinus Torvalds  * exists, return an uninitialized one.
8691da177e4SLinus Torvalds  *
8701da177e4SLinus Torvalds  */
nfs4_alloc_lock_state(struct nfs4_state * state,fl_owner_t fl_owner)8718003d3c4SJeff Layton static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
8721da177e4SLinus Torvalds {
8731da177e4SLinus Torvalds 	struct nfs4_lock_state *lsp;
87424d292b8SChuck Lever 	struct nfs_server *server = state->owner->so_server;
8751da177e4SLinus Torvalds 
8769c00fd9aSTrond Myklebust 	lsp = kzalloc(sizeof(*lsp), GFP_KERNEL_ACCOUNT);
8771da177e4SLinus Torvalds 	if (lsp == NULL)
8781da177e4SLinus Torvalds 		return NULL;
8797ba127abSTrond Myklebust 	nfs4_init_seqid_counter(&lsp->ls_seqid);
880194bc1f4SElena Reshetova 	refcount_set(&lsp->ls_count, 1);
881b64aec8dSTrond Myklebust 	lsp->ls_state = state;
8828003d3c4SJeff Layton 	lsp->ls_owner = fl_owner;
883724e2df9SBo Liu 	lsp->ls_seqid.owner_id = ida_alloc(&server->lockowner_id, GFP_KERNEL_ACCOUNT);
88448c22eb2STrond Myklebust 	if (lsp->ls_seqid.owner_id < 0)
885d2d7ce28STrond Myklebust 		goto out_free;
8868d0a8a9dSTrond Myklebust 	INIT_LIST_HEAD(&lsp->ls_locks);
8871da177e4SLinus Torvalds 	return lsp;
888d2d7ce28STrond Myklebust out_free:
889d2d7ce28STrond Myklebust 	kfree(lsp);
890d2d7ce28STrond Myklebust 	return NULL;
8911da177e4SLinus Torvalds }
8921da177e4SLinus Torvalds 
nfs4_free_lock_state(struct nfs_server * server,struct nfs4_lock_state * lsp)8935ae67c4fSTrond Myklebust void nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
8949f958ab8STrond Myklebust {
895724e2df9SBo Liu 	ida_free(&server->lockowner_id, lsp->ls_seqid.owner_id);
8967ba127abSTrond Myklebust 	nfs4_destroy_seqid_counter(&lsp->ls_seqid);
8979f958ab8STrond Myklebust 	kfree(lsp);
8989f958ab8STrond Myklebust }
8999f958ab8STrond Myklebust 
9001da177e4SLinus Torvalds /*
9011da177e4SLinus Torvalds  * Return a compatible lock_state. If no initialized lock_state structure
9021da177e4SLinus Torvalds  * exists, return an uninitialized one.
9031da177e4SLinus Torvalds  *
9041da177e4SLinus Torvalds  */
nfs4_get_lock_state(struct nfs4_state * state,fl_owner_t owner)9058003d3c4SJeff Layton static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
9068d0a8a9dSTrond Myklebust {
9078d0a8a9dSTrond Myklebust 	struct nfs4_lock_state *lsp, *new = NULL;
9088d0a8a9dSTrond Myklebust 
9098d0a8a9dSTrond Myklebust 	for(;;) {
9108d0a8a9dSTrond Myklebust 		spin_lock(&state->state_lock);
91168e33bd6SWei Yongjun 		lsp = __nfs4_find_lock_state(state, owner, NULL);
9128d0a8a9dSTrond Myklebust 		if (lsp != NULL)
9138d0a8a9dSTrond Myklebust 			break;
9148d0a8a9dSTrond Myklebust 		if (new != NULL) {
9158d0a8a9dSTrond Myklebust 			list_add(&new->ls_locks, &state->lock_states);
9168d0a8a9dSTrond Myklebust 			set_bit(LK_STATE_IN_USE, &state->flags);
9178d0a8a9dSTrond Myklebust 			lsp = new;
9188d0a8a9dSTrond Myklebust 			new = NULL;
9198d0a8a9dSTrond Myklebust 			break;
9208d0a8a9dSTrond Myklebust 		}
9218d0a8a9dSTrond Myklebust 		spin_unlock(&state->state_lock);
9228003d3c4SJeff Layton 		new = nfs4_alloc_lock_state(state, owner);
9238d0a8a9dSTrond Myklebust 		if (new == NULL)
9248d0a8a9dSTrond Myklebust 			return NULL;
9258d0a8a9dSTrond Myklebust 	}
9268d0a8a9dSTrond Myklebust 	spin_unlock(&state->state_lock);
9279f958ab8STrond Myklebust 	if (new != NULL)
9285ae67c4fSTrond Myklebust 		nfs4_free_lock_state(state->owner->so_server, new);
9298d0a8a9dSTrond Myklebust 	return lsp;
9308d0a8a9dSTrond Myklebust }
9318d0a8a9dSTrond Myklebust 
9328d0a8a9dSTrond Myklebust /*
9338d0a8a9dSTrond Myklebust  * Release reference to lock_state, and free it if we see that
9348d0a8a9dSTrond Myklebust  * it is no longer in use
9358d0a8a9dSTrond Myklebust  */
nfs4_put_lock_state(struct nfs4_lock_state * lsp)936faf5f49cSTrond Myklebust void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
9378d0a8a9dSTrond Myklebust {
938c8b2d0bfSTrond Myklebust 	struct nfs_server *server;
9398d0a8a9dSTrond Myklebust 	struct nfs4_state *state;
9408d0a8a9dSTrond Myklebust 
9418d0a8a9dSTrond Myklebust 	if (lsp == NULL)
9428d0a8a9dSTrond Myklebust 		return;
9438d0a8a9dSTrond Myklebust 	state = lsp->ls_state;
944194bc1f4SElena Reshetova 	if (!refcount_dec_and_lock(&lsp->ls_count, &state->state_lock))
9458d0a8a9dSTrond Myklebust 		return;
9468d0a8a9dSTrond Myklebust 	list_del(&lsp->ls_locks);
9478d0a8a9dSTrond Myklebust 	if (list_empty(&state->lock_states))
9488d0a8a9dSTrond Myklebust 		clear_bit(LK_STATE_IN_USE, &state->flags);
9498d0a8a9dSTrond Myklebust 	spin_unlock(&state->state_lock);
950c8b2d0bfSTrond Myklebust 	server = state->owner->so_server;
9510c0e0d3cSJeff Layton 	if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
9520c0e0d3cSJeff Layton 		struct nfs_client *clp = server->nfs_client;
9530c0e0d3cSJeff Layton 
9540c0e0d3cSJeff Layton 		clp->cl_mvops->free_lock_state(server, lsp);
9550c0e0d3cSJeff Layton 	} else
956c8b2d0bfSTrond Myklebust 		nfs4_free_lock_state(server, lsp);
9578d0a8a9dSTrond Myklebust }
9588d0a8a9dSTrond Myklebust 
nfs4_fl_copy_lock(struct file_lock * dst,struct file_lock * src)9598d0a8a9dSTrond Myklebust static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
9608d0a8a9dSTrond Myklebust {
9618d0a8a9dSTrond Myklebust 	struct nfs4_lock_state *lsp = src->fl_u.nfs4_fl.owner;
9628d0a8a9dSTrond Myklebust 
9638d0a8a9dSTrond Myklebust 	dst->fl_u.nfs4_fl.owner = lsp;
964194bc1f4SElena Reshetova 	refcount_inc(&lsp->ls_count);
9658d0a8a9dSTrond Myklebust }
9668d0a8a9dSTrond Myklebust 
nfs4_fl_release_lock(struct file_lock * fl)9678d0a8a9dSTrond Myklebust static void nfs4_fl_release_lock(struct file_lock *fl)
9688d0a8a9dSTrond Myklebust {
9698d0a8a9dSTrond Myklebust 	nfs4_put_lock_state(fl->fl_u.nfs4_fl.owner);
9708d0a8a9dSTrond Myklebust }
9718d0a8a9dSTrond Myklebust 
9726aed6285SAlexey Dobriyan static const struct file_lock_operations nfs4_fl_lock_ops = {
9738d0a8a9dSTrond Myklebust 	.fl_copy_lock = nfs4_fl_copy_lock,
9748d0a8a9dSTrond Myklebust 	.fl_release_private = nfs4_fl_release_lock,
9758d0a8a9dSTrond Myklebust };
9768d0a8a9dSTrond Myklebust 
nfs4_set_lock_state(struct nfs4_state * state,struct file_lock * fl)9778d0a8a9dSTrond Myklebust int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl)
9781da177e4SLinus Torvalds {
9791da177e4SLinus Torvalds 	struct nfs4_lock_state *lsp;
9801da177e4SLinus Torvalds 
9818d0a8a9dSTrond Myklebust 	if (fl->fl_ops != NULL)
9828d0a8a9dSTrond Myklebust 		return 0;
9838003d3c4SJeff Layton 	lsp = nfs4_get_lock_state(state, fl->fl_owner);
9841da177e4SLinus Torvalds 	if (lsp == NULL)
9858d0a8a9dSTrond Myklebust 		return -ENOMEM;
9868d0a8a9dSTrond Myklebust 	fl->fl_u.nfs4_fl.owner = lsp;
9878d0a8a9dSTrond Myklebust 	fl->fl_ops = &nfs4_fl_lock_ops;
9888d0a8a9dSTrond Myklebust 	return 0;
9891da177e4SLinus Torvalds }
9901da177e4SLinus Torvalds 
nfs4_copy_lock_stateid(nfs4_stateid * dst,struct nfs4_state * state,const struct nfs_lock_context * l_ctx)9915521abfdSTrond Myklebust static int nfs4_copy_lock_stateid(nfs4_stateid *dst,
9925521abfdSTrond Myklebust 		struct nfs4_state *state,
99317393475SNeilBrown 		const struct nfs_lock_context *l_ctx)
9941da177e4SLinus Torvalds {
9951da177e4SLinus Torvalds 	struct nfs4_lock_state *lsp;
9968d424431SNeilBrown 	fl_owner_t fl_owner, fl_flock_owner;
9975521abfdSTrond Myklebust 	int ret = -ENOENT;
9984fc8796dSTrond Myklebust 
99917393475SNeilBrown 	if (l_ctx == NULL)
10002a369153STrond Myklebust 		goto out;
10012a369153STrond Myklebust 
10024fc8796dSTrond Myklebust 	if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
10034fc8796dSTrond Myklebust 		goto out;
10044fc8796dSTrond Myklebust 
1005d51fdb87SNeilBrown 	fl_owner = l_ctx->lockowner;
10068d424431SNeilBrown 	fl_flock_owner = l_ctx->open_context->flock_owner;
10078d424431SNeilBrown 
10084fc8796dSTrond Myklebust 	spin_lock(&state->state_lock);
10098d424431SNeilBrown 	lsp = __nfs4_find_lock_state(state, fl_owner, fl_flock_owner);
1010ef1820f9SNeilBrown 	if (lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags))
1011ef1820f9SNeilBrown 		ret = -EIO;
1012ef1820f9SNeilBrown 	else if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) {
10134fc8796dSTrond Myklebust 		nfs4_stateid_copy(dst, &lsp->ls_stateid);
10145521abfdSTrond Myklebust 		ret = 0;
10154fc8796dSTrond Myklebust 	}
10164fc8796dSTrond Myklebust 	spin_unlock(&state->state_lock);
10174fc8796dSTrond Myklebust 	nfs4_put_lock_state(lsp);
10184fc8796dSTrond Myklebust out:
10194fc8796dSTrond Myklebust 	return ret;
10204fc8796dSTrond Myklebust }
10214fc8796dSTrond Myklebust 
nfs4_copy_open_stateid(nfs4_stateid * dst,struct nfs4_state * state)1022c82bac6fSTrond Myklebust bool nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
10234fc8796dSTrond Myklebust {
1024c82bac6fSTrond Myklebust 	bool ret;
102592b40e93STrond Myklebust 	const nfs4_stateid *src;
10268bda4e4cSTrond Myklebust 	int seq;
10271da177e4SLinus Torvalds 
10288bda4e4cSTrond Myklebust 	do {
1029c82bac6fSTrond Myklebust 		ret = false;
103092b40e93STrond Myklebust 		src = &zero_stateid;
10318bda4e4cSTrond Myklebust 		seq = read_seqbegin(&state->seqlock);
1032c82bac6fSTrond Myklebust 		if (test_bit(NFS_OPEN_STATE, &state->flags)) {
103392b40e93STrond Myklebust 			src = &state->open_stateid;
1034c82bac6fSTrond Myklebust 			ret = true;
1035c82bac6fSTrond Myklebust 		}
103692b40e93STrond Myklebust 		nfs4_stateid_copy(dst, src);
10378bda4e4cSTrond Myklebust 	} while (read_seqretry(&state->seqlock, seq));
1038c82bac6fSTrond Myklebust 	return ret;
10394fc8796dSTrond Myklebust }
10408d0a8a9dSTrond Myklebust 
10414fc8796dSTrond Myklebust /*
10424fc8796dSTrond Myklebust  * Byte-range lock aware utility to initialize the stateid of read/write
10434fc8796dSTrond Myklebust  * requests.
10444fc8796dSTrond Myklebust  */
nfs4_select_rw_stateid(struct nfs4_state * state,fmode_t fmode,const struct nfs_lock_context * l_ctx,nfs4_stateid * dst,const struct cred ** cred)1045abf4e13cSTrond Myklebust int nfs4_select_rw_stateid(struct nfs4_state *state,
104617393475SNeilBrown 		fmode_t fmode, const struct nfs_lock_context *l_ctx,
1047a52458b4SNeilBrown 		nfs4_stateid *dst, const struct cred **cred)
10484fc8796dSTrond Myklebust {
1049abf4e13cSTrond Myklebust 	int ret;
1050abf4e13cSTrond Myklebust 
10517ebeb7feSTrond Myklebust 	if (!nfs4_valid_open_stateid(state))
10527ebeb7feSTrond Myklebust 		return -EIO;
1053abf4e13cSTrond Myklebust 	if (cred != NULL)
1054abf4e13cSTrond Myklebust 		*cred = NULL;
105517393475SNeilBrown 	ret = nfs4_copy_lock_stateid(dst, state, l_ctx);
1056ef1820f9SNeilBrown 	if (ret == -EIO)
1057ef1820f9SNeilBrown 		/* A lost lock - don't even consider delegations */
1058ef1820f9SNeilBrown 		goto out;
1059146d70caSAndy Adamson 	/* returns true if delegation stateid found and copied */
1060abf4e13cSTrond Myklebust 	if (nfs4_copy_delegation_stateid(state->inode, fmode, dst, cred)) {
1061146d70caSAndy Adamson 		ret = 0;
10625521abfdSTrond Myklebust 		goto out;
1063146d70caSAndy Adamson 	}
10645521abfdSTrond Myklebust 	if (ret != -ENOENT)
1065ef1820f9SNeilBrown 		/* nfs4_copy_delegation_stateid() didn't over-write
1066ef1820f9SNeilBrown 		 * dst, so it still has the lock stateid which we now
1067ef1820f9SNeilBrown 		 * choose to use.
1068ef1820f9SNeilBrown 		 */
10695521abfdSTrond Myklebust 		goto out;
1070d9aba2b4STrond Myklebust 	ret = nfs4_copy_open_stateid(dst, state) ? 0 : -EAGAIN;
10715521abfdSTrond Myklebust out:
10723b66486cSTrond Myklebust 	if (nfs_server_capable(state->inode, NFS_CAP_STATEID_NFSV41))
10733b66486cSTrond Myklebust 		dst->seqid = 0;
10745521abfdSTrond Myklebust 	return ret;
10751da177e4SLinus Torvalds }
10761da177e4SLinus Torvalds 
nfs_alloc_seqid(struct nfs_seqid_counter * counter,gfp_t gfp_mask)10778535b2beSTrond Myklebust struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask)
10781da177e4SLinus Torvalds {
1079cee54fc9STrond Myklebust 	struct nfs_seqid *new;
1080cee54fc9STrond Myklebust 
10818535b2beSTrond Myklebust 	new = kmalloc(sizeof(*new), gfp_mask);
1082badc76ddSTrond Myklebust 	if (new == NULL)
1083badc76ddSTrond Myklebust 		return ERR_PTR(-ENOMEM);
1084cee54fc9STrond Myklebust 	new->sequence = counter;
10852f74c0a0STrond Myklebust 	INIT_LIST_HEAD(&new->list);
10864601df20STrond Myklebust 	new->task = NULL;
1087cee54fc9STrond Myklebust 	return new;
1088cee54fc9STrond Myklebust }
1089cee54fc9STrond Myklebust 
nfs_release_seqid(struct nfs_seqid * seqid)109072211dbeSTrond Myklebust void nfs_release_seqid(struct nfs_seqid *seqid)
1091cee54fc9STrond Myklebust {
10924601df20STrond Myklebust 	struct nfs_seqid_counter *sequence;
1093cee54fc9STrond Myklebust 
1094a6796419STrond Myklebust 	if (seqid == NULL || list_empty(&seqid->list))
10954601df20STrond Myklebust 		return;
10964601df20STrond Myklebust 	sequence = seqid->sequence;
1097cee54fc9STrond Myklebust 	spin_lock(&sequence->lock);
109872211dbeSTrond Myklebust 	list_del_init(&seqid->list);
10994601df20STrond Myklebust 	if (!list_empty(&sequence->list)) {
11004601df20STrond Myklebust 		struct nfs_seqid *next;
11014601df20STrond Myklebust 
11024601df20STrond Myklebust 		next = list_first_entry(&sequence->list,
11034601df20STrond Myklebust 				struct nfs_seqid, list);
11044601df20STrond Myklebust 		rpc_wake_up_queued_task(&sequence->wait, next->task);
11052f74c0a0STrond Myklebust 	}
11064601df20STrond Myklebust 	spin_unlock(&sequence->lock);
110772211dbeSTrond Myklebust }
110872211dbeSTrond Myklebust 
nfs_free_seqid(struct nfs_seqid * seqid)110972211dbeSTrond Myklebust void nfs_free_seqid(struct nfs_seqid *seqid)
111072211dbeSTrond Myklebust {
111172211dbeSTrond Myklebust 	nfs_release_seqid(seqid);
1112cee54fc9STrond Myklebust 	kfree(seqid);
11131da177e4SLinus Torvalds }
11141da177e4SLinus Torvalds 
11151da177e4SLinus Torvalds /*
11161da177e4SLinus Torvalds  * Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
11171da177e4SLinus Torvalds  * failed with a seqid incrementing error -
111816a6ddc7SToralf Förster  * see comments nfs4.h:seqid_mutating_error()
11191da177e4SLinus Torvalds  */
nfs_increment_seqid(int status,struct nfs_seqid * seqid)112088d90939STrond Myklebust static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
11211da177e4SLinus Torvalds {
1122cee54fc9STrond Myklebust 	switch (status) {
1123cee54fc9STrond Myklebust 		case 0:
1124cee54fc9STrond Myklebust 			break;
1125cee54fc9STrond Myklebust 		case -NFS4ERR_BAD_SEQID:
11266f43ddccSTrond Myklebust 			if (seqid->sequence->flags & NFS_SEQID_CONFIRMED)
11276f43ddccSTrond Myklebust 				return;
11289a3ba432STrond Myklebust 			pr_warn_ratelimited("NFS: v4 server returned a bad"
11296f43ddccSTrond Myklebust 					" sequence-id error on an"
11306f43ddccSTrond Myklebust 					" unconfirmed sequence %p!\n",
11316f43ddccSTrond Myklebust 					seqid->sequence);
1132ffb81717SGustavo A. R. Silva 			return;
1133cee54fc9STrond Myklebust 		case -NFS4ERR_STALE_CLIENTID:
1134cee54fc9STrond Myklebust 		case -NFS4ERR_STALE_STATEID:
1135cee54fc9STrond Myklebust 		case -NFS4ERR_BAD_STATEID:
1136cee54fc9STrond Myklebust 		case -NFS4ERR_BADXDR:
1137cee54fc9STrond Myklebust 		case -NFS4ERR_RESOURCE:
1138cee54fc9STrond Myklebust 		case -NFS4ERR_NOFILEHANDLE:
1139406dab84SChuck Lever 		case -NFS4ERR_MOVED:
1140cee54fc9STrond Myklebust 			/* Non-seqid mutating errors */
1141cee54fc9STrond Myklebust 			return;
11428b98a532Szhengbin 	}
1143cee54fc9STrond Myklebust 	/*
1144cee54fc9STrond Myklebust 	 * Note: no locking needed as we are guaranteed to be first
1145cee54fc9STrond Myklebust 	 * on the sequence list
1146cee54fc9STrond Myklebust 	 */
1147cee54fc9STrond Myklebust 	seqid->sequence->counter++;
1148cee54fc9STrond Myklebust }
1149cee54fc9STrond Myklebust 
nfs_increment_open_seqid(int status,struct nfs_seqid * seqid)1150cee54fc9STrond Myklebust void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
1151cee54fc9STrond Myklebust {
1152a6796419STrond Myklebust 	struct nfs4_state_owner *sp;
115334dc1ad7SBenny Halevy 
1154a6796419STrond Myklebust 	if (seqid == NULL)
1155a6796419STrond Myklebust 		return;
1156a6796419STrond Myklebust 
1157a6796419STrond Myklebust 	sp = container_of(seqid->sequence, struct nfs4_state_owner, so_seqid);
115834dc1ad7SBenny Halevy 	if (status == -NFS4ERR_BAD_SEQID)
115986cfb041SNeilBrown 		nfs4_reset_state_owner(sp);
1160a6796419STrond Myklebust 	if (!nfs4_has_session(sp->so_server->nfs_client))
116188d90939STrond Myklebust 		nfs_increment_seqid(status, seqid);
1162cee54fc9STrond Myklebust }
1163cee54fc9STrond Myklebust 
1164cee54fc9STrond Myklebust /*
1165cee54fc9STrond Myklebust  * Increment the seqid if the LOCK/LOCKU succeeded, or
1166cee54fc9STrond Myklebust  * failed with a seqid incrementing error -
116716a6ddc7SToralf Förster  * see comments nfs4.h:seqid_mutating_error()
1168cee54fc9STrond Myklebust  */
nfs_increment_lock_seqid(int status,struct nfs_seqid * seqid)1169cee54fc9STrond Myklebust void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid)
1170cee54fc9STrond Myklebust {
1171a6796419STrond Myklebust 	if (seqid != NULL)
117288d90939STrond Myklebust 		nfs_increment_seqid(status, seqid);
1173cee54fc9STrond Myklebust }
1174cee54fc9STrond Myklebust 
nfs_wait_on_sequence(struct nfs_seqid * seqid,struct rpc_task * task)1175cee54fc9STrond Myklebust int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task)
1176cee54fc9STrond Myklebust {
1177a6796419STrond Myklebust 	struct nfs_seqid_counter *sequence;
1178cee54fc9STrond Myklebust 	int status = 0;
1179cee54fc9STrond Myklebust 
1180a6796419STrond Myklebust 	if (seqid == NULL)
1181a6796419STrond Myklebust 		goto out;
1182a6796419STrond Myklebust 	sequence = seqid->sequence;
1183cee54fc9STrond Myklebust 	spin_lock(&sequence->lock);
11844601df20STrond Myklebust 	seqid->task = task;
11852f74c0a0STrond Myklebust 	if (list_empty(&seqid->list))
11862f74c0a0STrond Myklebust 		list_add_tail(&seqid->list, &sequence->list);
11872f74c0a0STrond Myklebust 	if (list_first_entry(&sequence->list, struct nfs_seqid, list) == seqid)
11882f74c0a0STrond Myklebust 		goto unlock;
11895d00837bSTrond Myklebust 	rpc_sleep_on(&sequence->wait, task, NULL);
1190cee54fc9STrond Myklebust 	status = -EAGAIN;
11912f74c0a0STrond Myklebust unlock:
1192cee54fc9STrond Myklebust 	spin_unlock(&sequence->lock);
1193a6796419STrond Myklebust out:
1194cee54fc9STrond Myklebust 	return status;
1195cee54fc9STrond Myklebust }
11961da177e4SLinus Torvalds 
1197e005e804STrond Myklebust static int nfs4_run_state_manager(void *);
11981da177e4SLinus Torvalds 
nfs4_clear_state_manager_bit(struct nfs_client * clp)1199e005e804STrond Myklebust static void nfs4_clear_state_manager_bit(struct nfs_client *clp)
1200433fbe4cSTrond Myklebust {
120143d20e80STrond Myklebust 	clear_and_wake_up_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
1202433fbe4cSTrond Myklebust 	rpc_wake_up(&clp->cl_rpcwaitq);
1203433fbe4cSTrond Myklebust }
1204433fbe4cSTrond Myklebust 
12051da177e4SLinus Torvalds /*
1206e005e804STrond Myklebust  * Schedule the nfs_client asynchronous state management routine
12071da177e4SLinus Torvalds  */
nfs4_schedule_state_manager(struct nfs_client * clp)1208b0d3ded1STrond Myklebust void nfs4_schedule_state_manager(struct nfs_client *clp)
12091da177e4SLinus Torvalds {
12105043e900STrond Myklebust 	struct task_struct *task;
12112446ab60STrond Myklebust 	char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
1212956fd46fSTrond Myklebust 	struct rpc_clnt *clnt = clp->cl_rpcclient;
1213956fd46fSTrond Myklebust 	bool swapon = false;
12141da177e4SLinus Torvalds 
1215956fd46fSTrond Myklebust 	if (clnt->cl_shutdown)
12166ad477a6SBenjamin Coddington 		return;
12176ad477a6SBenjamin Coddington 
1218aeabb3c9STrond Myklebust 	set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
1219956fd46fSTrond Myklebust 
1220956fd46fSTrond Myklebust 	if (atomic_read(&clnt->cl_swapper)) {
1221956fd46fSTrond Myklebust 		swapon = !test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE,
1222956fd46fSTrond Myklebust 					   &clp->cl_state);
1223956fd46fSTrond Myklebust 		if (!swapon) {
12244dc73c67SNeilBrown 			wake_up_var(&clp->cl_state);
1225e005e804STrond Myklebust 			return;
12264dc73c67SNeilBrown 		}
1227956fd46fSTrond Myklebust 	}
1228956fd46fSTrond Myklebust 
1229956fd46fSTrond Myklebust 	if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
1230956fd46fSTrond Myklebust 		return;
1231956fd46fSTrond Myklebust 
12325043e900STrond Myklebust 	__module_get(THIS_MODULE);
1233212bf41dSElena Reshetova 	refcount_inc(&clp->cl_count);
12342446ab60STrond Myklebust 
12352446ab60STrond Myklebust 	/* The rcu_read_lock() is not strictly necessary, as the state
12362446ab60STrond Myklebust 	 * manager is the only thread that ever changes the rpc_xprt
12372446ab60STrond Myklebust 	 * after it's initialized.  At this point, we're single threaded. */
12382446ab60STrond Myklebust 	rcu_read_lock();
12392446ab60STrond Myklebust 	snprintf(buf, sizeof(buf), "%s-manager",
12402446ab60STrond Myklebust 			rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
12412446ab60STrond Myklebust 	rcu_read_unlock();
1242f170168bSKees Cook 	task = kthread_run(nfs4_run_state_manager, clp, "%s", buf);
12432446ab60STrond Myklebust 	if (IS_ERR(task)) {
12442446ab60STrond Myklebust 		printk(KERN_ERR "%s: kthread_run: %ld\n",
12452446ab60STrond Myklebust 			__func__, PTR_ERR(task));
1246b4e4f669STrond Myklebust 		if (!nfs_client_init_is_complete(clp))
1247b4e4f669STrond Myklebust 			nfs_mark_client_ready(clp, PTR_ERR(task));
1248956fd46fSTrond Myklebust 		if (swapon)
12494dc73c67SNeilBrown 			clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
1250956fd46fSTrond Myklebust 		nfs4_clear_state_manager_bit(clp);
125124c8dbbbSDavid Howells 		nfs_put_client(clp);
12525043e900STrond Myklebust 		module_put(THIS_MODULE);
12531da177e4SLinus Torvalds 	}
12542446ab60STrond Myklebust }
12551da177e4SLinus Torvalds 
12561da177e4SLinus Torvalds /*
12570400a6b0STrond Myklebust  * Schedule a lease recovery attempt
12581da177e4SLinus Torvalds  */
nfs4_schedule_lease_recovery(struct nfs_client * clp)12590400a6b0STrond Myklebust void nfs4_schedule_lease_recovery(struct nfs_client *clp)
12601da177e4SLinus Torvalds {
12611da177e4SLinus Torvalds 	if (!clp)
12621da177e4SLinus Torvalds 		return;
1263e598d843STrond Myklebust 	if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
1264e598d843STrond Myklebust 		set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
1265cc0a9843STrond Myklebust 	dprintk("%s: scheduling lease recovery for server %s\n", __func__,
1266cc0a9843STrond Myklebust 			clp->cl_hostname);
1267e005e804STrond Myklebust 	nfs4_schedule_state_manager(clp);
12681da177e4SLinus Torvalds }
12699cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs4_schedule_lease_recovery);
12701da177e4SLinus Torvalds 
1271c9fdeb28SChuck Lever /**
1272c9fdeb28SChuck Lever  * nfs4_schedule_migration_recovery - trigger migration recovery
1273c9fdeb28SChuck Lever  *
1274c9fdeb28SChuck Lever  * @server: FSID that is migrating
1275c9fdeb28SChuck Lever  *
1276c9fdeb28SChuck Lever  * Returns zero if recovery has started, otherwise a negative NFS4ERR
1277c9fdeb28SChuck Lever  * value is returned.
1278c9fdeb28SChuck Lever  */
nfs4_schedule_migration_recovery(const struct nfs_server * server)1279c9fdeb28SChuck Lever int nfs4_schedule_migration_recovery(const struct nfs_server *server)
1280c9fdeb28SChuck Lever {
1281c9fdeb28SChuck Lever 	struct nfs_client *clp = server->nfs_client;
1282c9fdeb28SChuck Lever 
1283c9fdeb28SChuck Lever 	if (server->fh_expire_type != NFS4_FH_PERSISTENT) {
1284c9fdeb28SChuck Lever 		pr_err("NFS: volatile file handles not supported (server %s)\n",
1285c9fdeb28SChuck Lever 				clp->cl_hostname);
1286c9fdeb28SChuck Lever 		return -NFS4ERR_IO;
1287c9fdeb28SChuck Lever 	}
1288c9fdeb28SChuck Lever 
1289c9fdeb28SChuck Lever 	if (test_bit(NFS_MIG_FAILED, &server->mig_status))
1290c9fdeb28SChuck Lever 		return -NFS4ERR_IO;
1291c9fdeb28SChuck Lever 
1292c9fdeb28SChuck Lever 	dprintk("%s: scheduling migration recovery for (%llx:%llx) on %s\n",
1293c9fdeb28SChuck Lever 			__func__,
1294c9fdeb28SChuck Lever 			(unsigned long long)server->fsid.major,
1295c9fdeb28SChuck Lever 			(unsigned long long)server->fsid.minor,
1296c9fdeb28SChuck Lever 			clp->cl_hostname);
1297c9fdeb28SChuck Lever 
1298c9fdeb28SChuck Lever 	set_bit(NFS_MIG_IN_TRANSITION,
1299c9fdeb28SChuck Lever 			&((struct nfs_server *)server)->mig_status);
1300c9fdeb28SChuck Lever 	set_bit(NFS4CLNT_MOVED, &clp->cl_state);
1301c9fdeb28SChuck Lever 
1302c9fdeb28SChuck Lever 	nfs4_schedule_state_manager(clp);
1303c9fdeb28SChuck Lever 	return 0;
1304c9fdeb28SChuck Lever }
1305c9fdeb28SChuck Lever EXPORT_SYMBOL_GPL(nfs4_schedule_migration_recovery);
1306c9fdeb28SChuck Lever 
1307b7f7a66eSChuck Lever /**
1308b7f7a66eSChuck Lever  * nfs4_schedule_lease_moved_recovery - start lease-moved recovery
1309b7f7a66eSChuck Lever  *
1310b7f7a66eSChuck Lever  * @clp: server to check for moved leases
1311b7f7a66eSChuck Lever  *
1312b7f7a66eSChuck Lever  */
nfs4_schedule_lease_moved_recovery(struct nfs_client * clp)1313b7f7a66eSChuck Lever void nfs4_schedule_lease_moved_recovery(struct nfs_client *clp)
1314b7f7a66eSChuck Lever {
1315b7f7a66eSChuck Lever 	dprintk("%s: scheduling lease-moved recovery for client ID %llx on %s\n",
1316b7f7a66eSChuck Lever 		__func__, clp->cl_clientid, clp->cl_hostname);
1317b7f7a66eSChuck Lever 
1318b7f7a66eSChuck Lever 	set_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state);
1319b7f7a66eSChuck Lever 	nfs4_schedule_state_manager(clp);
1320b7f7a66eSChuck Lever }
1321b7f7a66eSChuck Lever EXPORT_SYMBOL_GPL(nfs4_schedule_lease_moved_recovery);
1322b7f7a66eSChuck Lever 
nfs4_wait_clnt_recover(struct nfs_client * clp)132333021279STrond Myklebust int nfs4_wait_clnt_recover(struct nfs_client *clp)
132433021279STrond Myklebust {
132533021279STrond Myklebust 	int res;
132633021279STrond Myklebust 
132733021279STrond Myklebust 	might_sleep();
132833021279STrond Myklebust 
1329212bf41dSElena Reshetova 	refcount_inc(&clp->cl_count);
133074316201SNeilBrown 	res = wait_on_bit_action(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
1331f5d39b02SPeter Zijlstra 				 nfs_wait_bit_killable,
1332f5d39b02SPeter Zijlstra 				 TASK_KILLABLE|TASK_FREEZABLE_UNSAFE);
133333021279STrond Myklebust 	if (res)
13340625c2ddSChuck Lever 		goto out;
133533021279STrond Myklebust 	if (clp->cl_cons_state < 0)
13360625c2ddSChuck Lever 		res = clp->cl_cons_state;
13370625c2ddSChuck Lever out:
13380625c2ddSChuck Lever 	nfs_put_client(clp);
13390625c2ddSChuck Lever 	return res;
134033021279STrond Myklebust }
134133021279STrond Myklebust 
nfs4_client_recover_expired_lease(struct nfs_client * clp)134233021279STrond Myklebust int nfs4_client_recover_expired_lease(struct nfs_client *clp)
134333021279STrond Myklebust {
134433021279STrond Myklebust 	unsigned int loop;
134533021279STrond Myklebust 	int ret;
134633021279STrond Myklebust 
134733021279STrond Myklebust 	for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
134833021279STrond Myklebust 		ret = nfs4_wait_clnt_recover(clp);
134933021279STrond Myklebust 		if (ret != 0)
135033021279STrond Myklebust 			break;
135133021279STrond Myklebust 		if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
135233021279STrond Myklebust 		    !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
135333021279STrond Myklebust 			break;
135433021279STrond Myklebust 		nfs4_schedule_state_manager(clp);
135533021279STrond Myklebust 		ret = -EIO;
135633021279STrond Myklebust 	}
135733021279STrond Myklebust 	return ret;
135833021279STrond Myklebust }
135933021279STrond Myklebust 
1360ad1e3968STrond Myklebust /*
1361ad1e3968STrond Myklebust  * nfs40_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN
1362ad1e3968STrond Myklebust  * @clp: client to process
1363ad1e3968STrond Myklebust  *
1364ad1e3968STrond Myklebust  * Set the NFS4CLNT_LEASE_EXPIRED state in order to force a
1365ad1e3968STrond Myklebust  * resend of the SETCLIENTID and hence re-establish the
1366ad1e3968STrond Myklebust  * callback channel. Then return all existing delegations.
1367ad1e3968STrond Myklebust  */
nfs40_handle_cb_pathdown(struct nfs_client * clp)1368ad1e3968STrond Myklebust static void nfs40_handle_cb_pathdown(struct nfs_client *clp)
1369ad1e3968STrond Myklebust {
1370ad1e3968STrond Myklebust 	set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
1371ad1e3968STrond Myklebust 	nfs_expire_all_delegations(clp);
1372cc0a9843STrond Myklebust 	dprintk("%s: handling CB_PATHDOWN recovery for server %s\n", __func__,
1373cc0a9843STrond Myklebust 			clp->cl_hostname);
1374ad1e3968STrond Myklebust }
1375ad1e3968STrond Myklebust 
nfs4_schedule_path_down_recovery(struct nfs_client * clp)1376042b60beSTrond Myklebust void nfs4_schedule_path_down_recovery(struct nfs_client *clp)
1377042b60beSTrond Myklebust {
1378ad1e3968STrond Myklebust 	nfs40_handle_cb_pathdown(clp);
1379042b60beSTrond Myklebust 	nfs4_schedule_state_manager(clp);
1380042b60beSTrond Myklebust }
1381042b60beSTrond Myklebust 
nfs4_state_mark_reclaim_reboot(struct nfs_client * clp,struct nfs4_state * state)1382f9feab1eSTrond Myklebust static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
1383b79a4a1bSTrond Myklebust {
1384b79a4a1bSTrond Myklebust 
13857ebeb7feSTrond Myklebust 	if (!nfs4_valid_open_stateid(state))
13867ebeb7feSTrond Myklebust 		return 0;
1387b79a4a1bSTrond Myklebust 	set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
1388b79a4a1bSTrond Myklebust 	/* Don't recover state that expired before the reboot */
1389b79a4a1bSTrond Myklebust 	if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) {
1390b79a4a1bSTrond Myklebust 		clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
1391b79a4a1bSTrond Myklebust 		return 0;
1392b79a4a1bSTrond Myklebust 	}
13937eff03aeSTrond Myklebust 	set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags);
1394b79a4a1bSTrond Myklebust 	set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
1395b79a4a1bSTrond Myklebust 	return 1;
1396b79a4a1bSTrond Myklebust }
1397b79a4a1bSTrond Myklebust 
nfs4_state_mark_reclaim_nograce(struct nfs_client * clp,struct nfs4_state * state)13984f14c194STrond Myklebust int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
1399b79a4a1bSTrond Myklebust {
14007ebeb7feSTrond Myklebust 	if (!nfs4_valid_open_stateid(state))
14017ebeb7feSTrond Myklebust 		return 0;
1402b79a4a1bSTrond Myklebust 	set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
1403b79a4a1bSTrond Myklebust 	clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
14047eff03aeSTrond Myklebust 	set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags);
1405b79a4a1bSTrond Myklebust 	set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
1406b79a4a1bSTrond Myklebust 	return 1;
1407b79a4a1bSTrond Myklebust }
1408b79a4a1bSTrond Myklebust 
nfs4_schedule_stateid_recovery(const struct nfs_server * server,struct nfs4_state * state)14095d422301STrond Myklebust int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state)
14100400a6b0STrond Myklebust {
14110400a6b0STrond Myklebust 	struct nfs_client *clp = server->nfs_client;
14120400a6b0STrond Myklebust 
14137ebeb7feSTrond Myklebust 	if (!nfs4_state_mark_reclaim_nograce(clp, state))
14145d422301STrond Myklebust 		return -EBADF;
1415994b15b9STrond Myklebust 	nfs_inode_find_delegation_state_and_recover(state->inode,
1416994b15b9STrond Myklebust 			&state->stateid);
1417cc0a9843STrond Myklebust 	dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
1418cc0a9843STrond Myklebust 			clp->cl_hostname);
14190400a6b0STrond Myklebust 	nfs4_schedule_state_manager(clp);
14205d422301STrond Myklebust 	return 0;
14210400a6b0STrond Myklebust }
14229cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery);
14230400a6b0STrond Myklebust 
14246c2d8f8dSTrond Myklebust static struct nfs4_lock_state *
nfs_state_find_lock_state_by_stateid(struct nfs4_state * state,const nfs4_stateid * stateid)14256c2d8f8dSTrond Myklebust nfs_state_find_lock_state_by_stateid(struct nfs4_state *state,
14266c2d8f8dSTrond Myklebust 		const nfs4_stateid *stateid)
14276c2d8f8dSTrond Myklebust {
14286c2d8f8dSTrond Myklebust 	struct nfs4_lock_state *pos;
14296c2d8f8dSTrond Myklebust 
14306c2d8f8dSTrond Myklebust 	list_for_each_entry(pos, &state->lock_states, ls_locks) {
14316c2d8f8dSTrond Myklebust 		if (!test_bit(NFS_LOCK_INITIALIZED, &pos->ls_flags))
14326c2d8f8dSTrond Myklebust 			continue;
143342c304c3STrond Myklebust 		if (nfs4_stateid_match_or_older(&pos->ls_stateid, stateid))
14346c2d8f8dSTrond Myklebust 			return pos;
14356c2d8f8dSTrond Myklebust 	}
14366c2d8f8dSTrond Myklebust 	return NULL;
14376c2d8f8dSTrond Myklebust }
14386c2d8f8dSTrond Myklebust 
nfs_state_lock_state_matches_stateid(struct nfs4_state * state,const nfs4_stateid * stateid)14396c2d8f8dSTrond Myklebust static bool nfs_state_lock_state_matches_stateid(struct nfs4_state *state,
14406c2d8f8dSTrond Myklebust 		const nfs4_stateid *stateid)
14416c2d8f8dSTrond Myklebust {
14426c2d8f8dSTrond Myklebust 	bool found = false;
14436c2d8f8dSTrond Myklebust 
14446c2d8f8dSTrond Myklebust 	if (test_bit(LK_STATE_IN_USE, &state->flags)) {
14456c2d8f8dSTrond Myklebust 		spin_lock(&state->state_lock);
14466c2d8f8dSTrond Myklebust 		if (nfs_state_find_lock_state_by_stateid(state, stateid))
14476c2d8f8dSTrond Myklebust 			found = true;
14486c2d8f8dSTrond Myklebust 		spin_unlock(&state->state_lock);
14496c2d8f8dSTrond Myklebust 	}
14506c2d8f8dSTrond Myklebust 	return found;
14516c2d8f8dSTrond Myklebust }
14526c2d8f8dSTrond Myklebust 
nfs_inode_find_state_and_recover(struct inode * inode,const nfs4_stateid * stateid)1453a1d0b5eeSTrond Myklebust void nfs_inode_find_state_and_recover(struct inode *inode,
1454a1d0b5eeSTrond Myklebust 		const nfs4_stateid *stateid)
1455a1d0b5eeSTrond Myklebust {
1456a1d0b5eeSTrond Myklebust 	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
1457a1d0b5eeSTrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
1458a1d0b5eeSTrond Myklebust 	struct nfs_open_context *ctx;
1459a1d0b5eeSTrond Myklebust 	struct nfs4_state *state;
1460a1d0b5eeSTrond Myklebust 	bool found = false;
1461a1d0b5eeSTrond Myklebust 
14620de43976STrond Myklebust 	rcu_read_lock();
14630de43976STrond Myklebust 	list_for_each_entry_rcu(ctx, &nfsi->open_files, list) {
1464a1d0b5eeSTrond Myklebust 		state = ctx->state;
1465a1d0b5eeSTrond Myklebust 		if (state == NULL)
1466a1d0b5eeSTrond Myklebust 			continue;
146742c304c3STrond Myklebust 		if (nfs4_stateid_match_or_older(&state->stateid, stateid) &&
14687ebeb7feSTrond Myklebust 		    nfs4_state_mark_reclaim_nograce(clp, state)) {
14696c2d8f8dSTrond Myklebust 			found = true;
1470a1d0b5eeSTrond Myklebust 			continue;
14716c2d8f8dSTrond Myklebust 		}
147242c304c3STrond Myklebust 		if (test_bit(NFS_OPEN_STATE, &state->flags) &&
147342c304c3STrond Myklebust 		    nfs4_stateid_match_or_older(&state->open_stateid, stateid) &&
147446280d9dSTrond Myklebust 		    nfs4_state_mark_reclaim_nograce(clp, state)) {
147546280d9dSTrond Myklebust 			found = true;
147646280d9dSTrond Myklebust 			continue;
147746280d9dSTrond Myklebust 		}
14787ebeb7feSTrond Myklebust 		if (nfs_state_lock_state_matches_stateid(state, stateid) &&
14797ebeb7feSTrond Myklebust 		    nfs4_state_mark_reclaim_nograce(clp, state))
1480a1d0b5eeSTrond Myklebust 			found = true;
1481a1d0b5eeSTrond Myklebust 	}
14820de43976STrond Myklebust 	rcu_read_unlock();
14836c2d8f8dSTrond Myklebust 
14846c2d8f8dSTrond Myklebust 	nfs_inode_find_delegation_state_and_recover(inode, stateid);
1485a1d0b5eeSTrond Myklebust 	if (found)
1486a1d0b5eeSTrond Myklebust 		nfs4_schedule_state_manager(clp);
1487a1d0b5eeSTrond Myklebust }
1488a1d0b5eeSTrond Myklebust 
nfs4_state_mark_open_context_bad(struct nfs4_state * state,int err)148986dbd08bSTrond Myklebust static void nfs4_state_mark_open_context_bad(struct nfs4_state *state, int err)
1490c58c8441STrond Myklebust {
1491c58c8441STrond Myklebust 	struct inode *inode = state->inode;
1492c58c8441STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
1493c58c8441STrond Myklebust 	struct nfs_open_context *ctx;
1494c58c8441STrond Myklebust 
14950de43976STrond Myklebust 	rcu_read_lock();
14960de43976STrond Myklebust 	list_for_each_entry_rcu(ctx, &nfsi->open_files, list) {
1497c58c8441STrond Myklebust 		if (ctx->state != state)
1498c58c8441STrond Myklebust 			continue;
1499c58c8441STrond Myklebust 		set_bit(NFS_CONTEXT_BAD, &ctx->flags);
150086dbd08bSTrond Myklebust 		pr_warn("NFSv4: state recovery failed for open file %pd2, "
150186dbd08bSTrond Myklebust 				"error = %d\n", ctx->dentry, err);
1502c58c8441STrond Myklebust 	}
15030de43976STrond Myklebust 	rcu_read_unlock();
1504c58c8441STrond Myklebust }
1505c58c8441STrond Myklebust 
nfs4_state_mark_recovery_failed(struct nfs4_state * state,int error)15065d422301STrond Myklebust static void nfs4_state_mark_recovery_failed(struct nfs4_state *state, int error)
15075d422301STrond Myklebust {
15085d422301STrond Myklebust 	set_bit(NFS_STATE_RECOVERY_FAILED, &state->flags);
150986dbd08bSTrond Myklebust 	nfs4_state_mark_open_context_bad(state, error);
15105d422301STrond Myklebust }
15115d422301STrond Myklebust 
1512a1d0b5eeSTrond Myklebust 
nfs4_reclaim_locks(struct nfs4_state * state,const struct nfs4_state_recovery_ops * ops)151302860014STrond Myklebust static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
15141da177e4SLinus Torvalds {
15151da177e4SLinus Torvalds 	struct inode *inode = state->inode;
151619e03c57STrond Myklebust 	struct nfs_inode *nfsi = NFS_I(inode);
15171da177e4SLinus Torvalds 	struct file_lock *fl;
1518dce2630cSNeilBrown 	struct nfs4_lock_state *lsp;
15191da177e4SLinus Torvalds 	int status = 0;
152017b985deSJeff Layton 	struct file_lock_context *flctx = locks_inode_context(inode);
1521bd61e0a9SJeff Layton 	struct list_head *list;
15221da177e4SLinus Torvalds 
1523bd61e0a9SJeff Layton 	if (flctx == NULL)
15243f09df70STrond Myklebust 		return 0;
15253f09df70STrond Myklebust 
1526bd61e0a9SJeff Layton 	list = &flctx->flc_posix;
1527bd61e0a9SJeff Layton 
15283f09df70STrond Myklebust 	/* Guard against delegation returns and new lock/unlock calls */
152919e03c57STrond Myklebust 	down_write(&nfsi->rwsem);
15306109c850SJeff Layton 	spin_lock(&flctx->flc_lock);
1531bd61e0a9SJeff Layton restart:
1532bd61e0a9SJeff Layton 	list_for_each_entry(fl, list, fl_list) {
15335263e31eSJeff Layton 		if (nfs_file_open_context(fl->fl_file)->state != state)
15345263e31eSJeff Layton 			continue;
15356109c850SJeff Layton 		spin_unlock(&flctx->flc_lock);
15365263e31eSJeff Layton 		status = ops->recover_lock(state, fl);
15375263e31eSJeff Layton 		switch (status) {
15385263e31eSJeff Layton 		case 0:
15395263e31eSJeff Layton 			break;
154067e7b52dSTrond Myklebust 		case -ETIMEDOUT:
15415263e31eSJeff Layton 		case -ESTALE:
15425263e31eSJeff Layton 		case -NFS4ERR_ADMIN_REVOKED:
15435263e31eSJeff Layton 		case -NFS4ERR_STALE_STATEID:
15445263e31eSJeff Layton 		case -NFS4ERR_BAD_STATEID:
15455263e31eSJeff Layton 		case -NFS4ERR_EXPIRED:
15465263e31eSJeff Layton 		case -NFS4ERR_NO_GRACE:
15475263e31eSJeff Layton 		case -NFS4ERR_STALE_CLIENTID:
15485263e31eSJeff Layton 		case -NFS4ERR_BADSESSION:
15495263e31eSJeff Layton 		case -NFS4ERR_BADSLOT:
15505263e31eSJeff Layton 		case -NFS4ERR_BAD_HIGH_SLOT:
15515263e31eSJeff Layton 		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
15525263e31eSJeff Layton 			goto out;
15535263e31eSJeff Layton 		default:
15545263e31eSJeff Layton 			pr_err("NFS: %s: unhandled error %d\n",
15555263e31eSJeff Layton 					__func__, status);
1556df561f66SGustavo A. R. Silva 			fallthrough;
15575263e31eSJeff Layton 		case -ENOMEM:
15585263e31eSJeff Layton 		case -NFS4ERR_DENIED:
15595263e31eSJeff Layton 		case -NFS4ERR_RECLAIM_BAD:
15605263e31eSJeff Layton 		case -NFS4ERR_RECLAIM_CONFLICT:
1561dce2630cSNeilBrown 			lsp = fl->fl_u.nfs4_fl.owner;
1562dce2630cSNeilBrown 			if (lsp)
1563dce2630cSNeilBrown 				set_bit(NFS_LOCK_LOST, &lsp->ls_flags);
15645263e31eSJeff Layton 			status = 0;
15655263e31eSJeff Layton 		}
15666109c850SJeff Layton 		spin_lock(&flctx->flc_lock);
15675263e31eSJeff Layton 	}
1568bd61e0a9SJeff Layton 	if (list == &flctx->flc_posix) {
1569bd61e0a9SJeff Layton 		list = &flctx->flc_flock;
1570bd61e0a9SJeff Layton 		goto restart;
1571bd61e0a9SJeff Layton 	}
15726109c850SJeff Layton 	spin_unlock(&flctx->flc_lock);
1573965b5d67STrond Myklebust out:
157419e03c57STrond Myklebust 	up_write(&nfsi->rwsem);
15751da177e4SLinus Torvalds 	return status;
15761da177e4SLinus Torvalds }
15771da177e4SLinus Torvalds 
157880f42368SAnna Schumaker #ifdef CONFIG_NFS_V4_2
nfs42_complete_copies(struct nfs4_state_owner * sp,struct nfs4_state * state)157980f42368SAnna Schumaker static void nfs42_complete_copies(struct nfs4_state_owner *sp, struct nfs4_state *state)
158080f42368SAnna Schumaker {
158180f42368SAnna Schumaker 	struct nfs4_copy_state *copy;
158280f42368SAnna Schumaker 
15830e65a32cSOlga Kornievskaia 	if (!test_bit(NFS_CLNT_DST_SSC_COPY_STATE, &state->flags) &&
15840e65a32cSOlga Kornievskaia 		!test_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags))
158580f42368SAnna Schumaker 		return;
158680f42368SAnna Schumaker 
158780f42368SAnna Schumaker 	spin_lock(&sp->so_server->nfs_client->cl_lock);
158880f42368SAnna Schumaker 	list_for_each_entry(copy, &sp->so_server->ss_copies, copies) {
15890e65a32cSOlga Kornievskaia 		if ((test_bit(NFS_CLNT_DST_SSC_COPY_STATE, &state->flags) &&
15900e65a32cSOlga Kornievskaia 				!nfs4_stateid_match_other(&state->stateid,
15910e65a32cSOlga Kornievskaia 				&copy->parent_dst_state->stateid)))
159280f42368SAnna Schumaker 				continue;
159380f42368SAnna Schumaker 		copy->flags = 1;
15940e65a32cSOlga Kornievskaia 		if (test_and_clear_bit(NFS_CLNT_DST_SSC_COPY_STATE,
15950e65a32cSOlga Kornievskaia 				&state->flags)) {
15960e65a32cSOlga Kornievskaia 			clear_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags);
159780f42368SAnna Schumaker 			complete(&copy->completion);
15980e65a32cSOlga Kornievskaia 		}
15990e65a32cSOlga Kornievskaia 	}
1600*fca41e5fSYanjun Zhang 	list_for_each_entry(copy, &sp->so_server->ss_src_copies, src_copies) {
16010e65a32cSOlga Kornievskaia 		if ((test_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags) &&
16020e65a32cSOlga Kornievskaia 				!nfs4_stateid_match_other(&state->stateid,
16030e65a32cSOlga Kornievskaia 				&copy->parent_src_state->stateid)))
16040e65a32cSOlga Kornievskaia 				continue;
16050e65a32cSOlga Kornievskaia 		copy->flags = 1;
16060e65a32cSOlga Kornievskaia 		if (test_and_clear_bit(NFS_CLNT_DST_SSC_COPY_STATE,
16070e65a32cSOlga Kornievskaia 				&state->flags))
16080e65a32cSOlga Kornievskaia 			complete(&copy->completion);
160980f42368SAnna Schumaker 	}
161080f42368SAnna Schumaker 	spin_unlock(&sp->so_server->nfs_client->cl_lock);
161180f42368SAnna Schumaker }
161280f42368SAnna Schumaker #else /* !CONFIG_NFS_V4_2 */
nfs42_complete_copies(struct nfs4_state_owner * sp,struct nfs4_state * state)161380f42368SAnna Schumaker static inline void nfs42_complete_copies(struct nfs4_state_owner *sp,
161480f42368SAnna Schumaker 					 struct nfs4_state *state)
161580f42368SAnna Schumaker {
161680f42368SAnna Schumaker }
161780f42368SAnna Schumaker #endif /* CONFIG_NFS_V4_2 */
161880f42368SAnna Schumaker 
__nfs4_reclaim_open_state(struct nfs4_state_owner * sp,struct nfs4_state * state,const struct nfs4_state_recovery_ops * ops,int * lost_locks)1619cb7a8384SAnna Schumaker static int __nfs4_reclaim_open_state(struct nfs4_state_owner *sp, struct nfs4_state *state,
16203e2910c7SNeilBrown 				     const struct nfs4_state_recovery_ops *ops,
16213e2910c7SNeilBrown 				     int *lost_locks)
1622cb7a8384SAnna Schumaker {
1623cb7a8384SAnna Schumaker 	struct nfs4_lock_state *lock;
1624cb7a8384SAnna Schumaker 	int status;
1625cb7a8384SAnna Schumaker 
1626cb7a8384SAnna Schumaker 	status = ops->recover_open(sp, state);
1627cb7a8384SAnna Schumaker 	if (status < 0)
1628cb7a8384SAnna Schumaker 		return status;
1629cb7a8384SAnna Schumaker 
1630cb7a8384SAnna Schumaker 	status = nfs4_reclaim_locks(state, ops);
1631cb7a8384SAnna Schumaker 	if (status < 0)
1632cb7a8384SAnna Schumaker 		return status;
1633cb7a8384SAnna Schumaker 
1634cb7a8384SAnna Schumaker 	if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) {
1635cb7a8384SAnna Schumaker 		spin_lock(&state->state_lock);
1636cb7a8384SAnna Schumaker 		list_for_each_entry(lock, &state->lock_states, ls_locks) {
163721f86d2dSChuck Lever 			trace_nfs4_state_lock_reclaim(state, lock);
1638ef8d98f2SNeilBrown 			if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags) &&
1639ef8d98f2SNeilBrown 			    !test_bit(NFS_LOCK_UNLOCKING, &lock->ls_flags))
16403e2910c7SNeilBrown 				*lost_locks += 1;
1641cb7a8384SAnna Schumaker 		}
1642cb7a8384SAnna Schumaker 		spin_unlock(&state->state_lock);
1643cb7a8384SAnna Schumaker 	}
1644cb7a8384SAnna Schumaker 
164580f42368SAnna Schumaker 	nfs42_complete_copies(sp, state);
1646cb7a8384SAnna Schumaker 	clear_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
1647cb7a8384SAnna Schumaker 	return status;
1648cb7a8384SAnna Schumaker }
1649cb7a8384SAnna Schumaker 
nfs4_reclaim_open_state(struct nfs4_state_owner * sp,const struct nfs4_state_recovery_ops * ops,int * lost_locks)16503e2910c7SNeilBrown static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp,
16513e2910c7SNeilBrown 				   const struct nfs4_state_recovery_ops *ops,
16523e2910c7SNeilBrown 				   int *lost_locks)
16531da177e4SLinus Torvalds {
16541da177e4SLinus Torvalds 	struct nfs4_state *state;
1655c34fae00STrond Myklebust 	unsigned int loop = 0;
16561da177e4SLinus Torvalds 	int status = 0;
16570b9018b9SOlga Kornievskaia #ifdef CONFIG_NFS_V4_2
16580b9018b9SOlga Kornievskaia 	bool found_ssc_copy_state = false;
16590b9018b9SOlga Kornievskaia #endif /* CONFIG_NFS_V4_2 */
16601da177e4SLinus Torvalds 
16611da177e4SLinus Torvalds 	/* Note: we rely on the sp->so_states list being ordered
16621da177e4SLinus Torvalds 	 * so that we always reclaim open(O_RDWR) and/or open(O_WRITE)
16631da177e4SLinus Torvalds 	 * states first.
16641da177e4SLinus Torvalds 	 * This is needed to ensure that the server won't give us any
16651da177e4SLinus Torvalds 	 * read delegations that we have to return if, say, we are
16661da177e4SLinus Torvalds 	 * recovering after a network partition or a reboot from a
16671da177e4SLinus Torvalds 	 * server that doesn't support a grace period.
16681da177e4SLinus Torvalds 	 */
1669fe1d8195STrond Myklebust 	spin_lock(&sp->so_lock);
1670abbec2daSTrond Myklebust 	raw_write_seqcount_begin(&sp->so_reclaim_seqcount);
1671c137afabSTrond Myklebust restart:
16721da177e4SLinus Torvalds 	list_for_each_entry(state, &sp->so_states, open_states) {
1673b79a4a1bSTrond Myklebust 		if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
1674b79a4a1bSTrond Myklebust 			continue;
16755d422301STrond Myklebust 		if (!nfs4_valid_open_stateid(state))
16765d422301STrond Myklebust 			continue;
16771da177e4SLinus Torvalds 		if (state->state == 0)
16781da177e4SLinus Torvalds 			continue;
16790b9018b9SOlga Kornievskaia #ifdef CONFIG_NFS_V4_2
16800b9018b9SOlga Kornievskaia 		if (test_bit(NFS_SRV_SSC_COPY_STATE, &state->flags)) {
16810b9018b9SOlga Kornievskaia 			nfs4_state_mark_recovery_failed(state, -EIO);
16820b9018b9SOlga Kornievskaia 			found_ssc_copy_state = true;
16830b9018b9SOlga Kornievskaia 			continue;
16840b9018b9SOlga Kornievskaia 		}
16850b9018b9SOlga Kornievskaia #endif /* CONFIG_NFS_V4_2 */
1686ace9fad4STrond Myklebust 		refcount_inc(&state->count);
1687fe1d8195STrond Myklebust 		spin_unlock(&sp->so_lock);
16883e2910c7SNeilBrown 		status = __nfs4_reclaim_open_state(sp, state, ops, lost_locks);
1689cb7a8384SAnna Schumaker 
16901da177e4SLinus Torvalds 		switch (status) {
16911da177e4SLinus Torvalds 		default:
1692c34fae00STrond Myklebust 			if (status >= 0) {
1693c34fae00STrond Myklebust 				loop = 0;
169435a61606SAnna Schumaker 				break;
1695c34fae00STrond Myklebust 			}
169635a61606SAnna Schumaker 			printk(KERN_ERR "NFS: %s: unhandled error %d\n", __func__, status);
1697df561f66SGustavo A. R. Silva 			fallthrough;
16981da177e4SLinus Torvalds 		case -ENOENT:
1699965b5d67STrond Myklebust 		case -ENOMEM:
1700304020feSTrond Myklebust 		case -EACCES:
1701304020feSTrond Myklebust 		case -EROFS:
1702304020feSTrond Myklebust 		case -EIO:
1703b79a4a1bSTrond Myklebust 		case -ESTALE:
17043660cd43SAndy Adamson 			/* Open state on this file cannot be recovered */
17055d422301STrond Myklebust 			nfs4_state_mark_recovery_failed(state, status);
17061da177e4SLinus Torvalds 			break;
170791876b13STrond Myklebust 		case -EAGAIN:
170891876b13STrond Myklebust 			ssleep(1);
1709c34fae00STrond Myklebust 			if (loop++ < 10) {
1710c34fae00STrond Myklebust 				set_bit(ops->state_flag_bit, &state->flags);
1711c34fae00STrond Myklebust 				break;
1712c34fae00STrond Myklebust 			}
1713df561f66SGustavo A. R. Silva 			fallthrough;
1714965b5d67STrond Myklebust 		case -NFS4ERR_ADMIN_REVOKED:
1715965b5d67STrond Myklebust 		case -NFS4ERR_STALE_STATEID:
1716d41cbfc9SBenjamin Coddington 		case -NFS4ERR_OLD_STATEID:
1717965b5d67STrond Myklebust 		case -NFS4ERR_BAD_STATEID:
1718b79a4a1bSTrond Myklebust 		case -NFS4ERR_RECLAIM_BAD:
1719b79a4a1bSTrond Myklebust 		case -NFS4ERR_RECLAIM_CONFLICT:
17201f0e890dSTrond Myklebust 			nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state);
1721b79a4a1bSTrond Myklebust 			break;
17221da177e4SLinus Torvalds 		case -NFS4ERR_EXPIRED:
17231da177e4SLinus Torvalds 		case -NFS4ERR_NO_GRACE:
17241f0e890dSTrond Myklebust 			nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state);
1725df561f66SGustavo A. R. Silva 			fallthrough;
17261da177e4SLinus Torvalds 		case -NFS4ERR_STALE_CLIENTID:
17279c4c761aSTrond Myklebust 		case -NFS4ERR_BADSESSION:
17289c4c761aSTrond Myklebust 		case -NFS4ERR_BADSLOT:
17299c4c761aSTrond Myklebust 		case -NFS4ERR_BAD_HIGH_SLOT:
17309c4c761aSTrond Myklebust 		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
173167e7b52dSTrond Myklebust 		case -ETIMEDOUT:
17321da177e4SLinus Torvalds 			goto out_err;
17331da177e4SLinus Torvalds 		}
1734fe1d8195STrond Myklebust 		nfs4_put_open_state(state);
1735c137afabSTrond Myklebust 		spin_lock(&sp->so_lock);
1736fe1d8195STrond Myklebust 		goto restart;
17371da177e4SLinus Torvalds 	}
1738abbec2daSTrond Myklebust 	raw_write_seqcount_end(&sp->so_reclaim_seqcount);
1739fe1d8195STrond Myklebust 	spin_unlock(&sp->so_lock);
17400b9018b9SOlga Kornievskaia #ifdef CONFIG_NFS_V4_2
17410b9018b9SOlga Kornievskaia 	if (found_ssc_copy_state)
17420b9018b9SOlga Kornievskaia 		return -EIO;
17430b9018b9SOlga Kornievskaia #endif /* CONFIG_NFS_V4_2 */
17441da177e4SLinus Torvalds 	return 0;
17451da177e4SLinus Torvalds out_err:
1746fe1d8195STrond Myklebust 	nfs4_put_open_state(state);
1747c137afabSTrond Myklebust 	spin_lock(&sp->so_lock);
1748abbec2daSTrond Myklebust 	raw_write_seqcount_end(&sp->so_reclaim_seqcount);
1749c137afabSTrond Myklebust 	spin_unlock(&sp->so_lock);
17501da177e4SLinus Torvalds 	return status;
17511da177e4SLinus Torvalds }
17521da177e4SLinus Torvalds 
nfs4_clear_open_state(struct nfs4_state * state)1753b79a4a1bSTrond Myklebust static void nfs4_clear_open_state(struct nfs4_state *state)
1754cee54fc9STrond Myklebust {
1755cee54fc9STrond Myklebust 	struct nfs4_lock_state *lock;
1756cee54fc9STrond Myklebust 
1757003707c7STrond Myklebust 	clear_bit(NFS_DELEGATED_STATE, &state->flags);
1758003707c7STrond Myklebust 	clear_bit(NFS_O_RDONLY_STATE, &state->flags);
1759003707c7STrond Myklebust 	clear_bit(NFS_O_WRONLY_STATE, &state->flags);
1760003707c7STrond Myklebust 	clear_bit(NFS_O_RDWR_STATE, &state->flags);
17614b44b40eSTrond Myklebust 	spin_lock(&state->state_lock);
1762cee54fc9STrond Myklebust 	list_for_each_entry(lock, &state->lock_states, ls_locks) {
1763cee54fc9STrond Myklebust 		lock->ls_seqid.flags = 0;
1764795a88c9STrond Myklebust 		clear_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags);
1765cee54fc9STrond Myklebust 	}
17664b44b40eSTrond Myklebust 	spin_unlock(&state->state_lock);
1767cee54fc9STrond Myklebust }
1768b79a4a1bSTrond Myklebust 
nfs4_reset_seqids(struct nfs_server * server,int (* mark_reclaim)(struct nfs_client * clp,struct nfs4_state * state))176924d292b8SChuck Lever static void nfs4_reset_seqids(struct nfs_server *server,
177024d292b8SChuck Lever 	int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
1771b79a4a1bSTrond Myklebust {
177224d292b8SChuck Lever 	struct nfs_client *clp = server->nfs_client;
1773b79a4a1bSTrond Myklebust 	struct nfs4_state_owner *sp;
1774b79a4a1bSTrond Myklebust 	struct rb_node *pos;
1775b79a4a1bSTrond Myklebust 	struct nfs4_state *state;
1776b79a4a1bSTrond Myklebust 
177724d292b8SChuck Lever 	spin_lock(&clp->cl_lock);
177824d292b8SChuck Lever 	for (pos = rb_first(&server->state_owners);
177924d292b8SChuck Lever 	     pos != NULL;
178024d292b8SChuck Lever 	     pos = rb_next(pos)) {
178124d292b8SChuck Lever 		sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
1782b79a4a1bSTrond Myklebust 		sp->so_seqid.flags = 0;
1783b79a4a1bSTrond Myklebust 		spin_lock(&sp->so_lock);
1784b79a4a1bSTrond Myklebust 		list_for_each_entry(state, &sp->so_states, open_states) {
1785b79a4a1bSTrond Myklebust 			if (mark_reclaim(clp, state))
1786b79a4a1bSTrond Myklebust 				nfs4_clear_open_state(state);
1787b79a4a1bSTrond Myklebust 		}
1788ec073428STrond Myklebust 		spin_unlock(&sp->so_lock);
1789cee54fc9STrond Myklebust 	}
179024d292b8SChuck Lever 	spin_unlock(&clp->cl_lock);
179124d292b8SChuck Lever }
179224d292b8SChuck Lever 
nfs4_state_mark_reclaim_helper(struct nfs_client * clp,int (* mark_reclaim)(struct nfs_client * clp,struct nfs4_state * state))179324d292b8SChuck Lever static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp,
179424d292b8SChuck Lever 	int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
179524d292b8SChuck Lever {
179624d292b8SChuck Lever 	struct nfs_server *server;
179724d292b8SChuck Lever 
179824d292b8SChuck Lever 	rcu_read_lock();
179924d292b8SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
180024d292b8SChuck Lever 		nfs4_reset_seqids(server, mark_reclaim);
180124d292b8SChuck Lever 	rcu_read_unlock();
1802cee54fc9STrond Myklebust }
1803cee54fc9STrond Myklebust 
nfs4_state_start_reclaim_reboot(struct nfs_client * clp)1804b79a4a1bSTrond Myklebust static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp)
1805b79a4a1bSTrond Myklebust {
1806e59679f2STrond Myklebust 	set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
1807b79a4a1bSTrond Myklebust 	/* Mark all delegations for reclaim */
1808b79a4a1bSTrond Myklebust 	nfs_delegation_mark_reclaim(clp);
1809b79a4a1bSTrond Myklebust 	nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot);
1810b79a4a1bSTrond Myklebust }
1811b79a4a1bSTrond Myklebust 
nfs4_reclaim_complete(struct nfs_client * clp,const struct nfs4_state_recovery_ops * ops,const struct cred * cred)18120048fdd0STrond Myklebust static int nfs4_reclaim_complete(struct nfs_client *clp,
1813965e9c23STrond Myklebust 				 const struct nfs4_state_recovery_ops *ops,
1814a52458b4SNeilBrown 				 const struct cred *cred)
1815fce5c838SRicardo Labiaga {
1816fce5c838SRicardo Labiaga 	/* Notify the server we're done reclaiming our state */
1817fce5c838SRicardo Labiaga 	if (ops->reclaim_complete)
18180048fdd0STrond Myklebust 		return ops->reclaim_complete(clp, cred);
18190048fdd0STrond Myklebust 	return 0;
1820fce5c838SRicardo Labiaga }
1821fce5c838SRicardo Labiaga 
nfs4_clear_reclaim_server(struct nfs_server * server)182224d292b8SChuck Lever static void nfs4_clear_reclaim_server(struct nfs_server *server)
1823b79a4a1bSTrond Myklebust {
182424d292b8SChuck Lever 	struct nfs_client *clp = server->nfs_client;
1825b79a4a1bSTrond Myklebust 	struct nfs4_state_owner *sp;
1826b79a4a1bSTrond Myklebust 	struct rb_node *pos;
1827b79a4a1bSTrond Myklebust 	struct nfs4_state *state;
1828b79a4a1bSTrond Myklebust 
182924d292b8SChuck Lever 	spin_lock(&clp->cl_lock);
183024d292b8SChuck Lever 	for (pos = rb_first(&server->state_owners);
183124d292b8SChuck Lever 	     pos != NULL;
183224d292b8SChuck Lever 	     pos = rb_next(pos)) {
183324d292b8SChuck Lever 		sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
1834b79a4a1bSTrond Myklebust 		spin_lock(&sp->so_lock);
1835b79a4a1bSTrond Myklebust 		list_for_each_entry(state, &sp->so_states, open_states) {
183624d292b8SChuck Lever 			if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT,
183724d292b8SChuck Lever 						&state->flags))
1838b79a4a1bSTrond Myklebust 				continue;
1839b79a4a1bSTrond Myklebust 			nfs4_state_mark_reclaim_nograce(clp, state);
1840b79a4a1bSTrond Myklebust 		}
1841b79a4a1bSTrond Myklebust 		spin_unlock(&sp->so_lock);
1842b79a4a1bSTrond Myklebust 	}
184324d292b8SChuck Lever 	spin_unlock(&clp->cl_lock);
184424d292b8SChuck Lever }
184524d292b8SChuck Lever 
nfs4_state_clear_reclaim_reboot(struct nfs_client * clp)184624d292b8SChuck Lever static int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp)
184724d292b8SChuck Lever {
184824d292b8SChuck Lever 	struct nfs_server *server;
184924d292b8SChuck Lever 
185024d292b8SChuck Lever 	if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
185124d292b8SChuck Lever 		return 0;
185224d292b8SChuck Lever 
185324d292b8SChuck Lever 	rcu_read_lock();
185424d292b8SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
185524d292b8SChuck Lever 		nfs4_clear_reclaim_server(server);
185624d292b8SChuck Lever 	rcu_read_unlock();
1857b79a4a1bSTrond Myklebust 
1858b79a4a1bSTrond Myklebust 	nfs_delegation_reap_unclaimed(clp);
18596eaa6149STrond Myklebust 	return 1;
18606eaa6149STrond Myklebust }
18616eaa6149STrond Myklebust 
nfs4_state_end_reclaim_reboot(struct nfs_client * clp)18626eaa6149STrond Myklebust static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
18636eaa6149STrond Myklebust {
1864965e9c23STrond Myklebust 	const struct nfs4_state_recovery_ops *ops;
1865a52458b4SNeilBrown 	const struct cred *cred;
18660048fdd0STrond Myklebust 	int err;
1867965e9c23STrond Myklebust 
18686eaa6149STrond Myklebust 	if (!nfs4_state_clear_reclaim_reboot(clp))
18696eaa6149STrond Myklebust 		return;
1870965e9c23STrond Myklebust 	ops = clp->cl_mvops->reboot_recovery_ops;
187173d8bde5SChuck Lever 	cred = nfs4_get_clid_cred(clp);
18720048fdd0STrond Myklebust 	err = nfs4_reclaim_complete(clp, ops, cred);
1873a52458b4SNeilBrown 	put_cred(cred);
18740048fdd0STrond Myklebust 	if (err == -NFS4ERR_CONN_NOT_BOUND_TO_SESSION)
18750048fdd0STrond Myklebust 		set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
1876b79a4a1bSTrond Myklebust }
1877b79a4a1bSTrond Myklebust 
nfs4_state_start_reclaim_nograce(struct nfs_client * clp)1878b79a4a1bSTrond Myklebust static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
1879b79a4a1bSTrond Myklebust {
188045870d69STrond Myklebust 	nfs_mark_test_expired_all_delegations(clp);
1881b79a4a1bSTrond Myklebust 	nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
1882b79a4a1bSTrond Myklebust }
1883b79a4a1bSTrond Myklebust 
nfs4_recovery_handle_error(struct nfs_client * clp,int error)18844f7cdf18STrond Myklebust static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
1885e598d843STrond Myklebust {
1886e598d843STrond Myklebust 	switch (error) {
18874f38e4aaSTrond Myklebust 	case 0:
18884f38e4aaSTrond Myklebust 		break;
1889e598d843STrond Myklebust 	case -NFS4ERR_CB_PATH_DOWN:
1890ad1e3968STrond Myklebust 		nfs40_handle_cb_pathdown(clp);
18914f38e4aaSTrond Myklebust 		break;
1892c8b7ae3dSTrond Myklebust 	case -NFS4ERR_NO_GRACE:
1893c8b7ae3dSTrond Myklebust 		nfs4_state_end_reclaim_reboot(clp);
18944f38e4aaSTrond Myklebust 		break;
1895e598d843STrond Myklebust 	case -NFS4ERR_STALE_CLIENTID:
1896e598d843STrond Myklebust 		set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
1897e598d843STrond Myklebust 		nfs4_state_start_reclaim_reboot(clp);
1898e598d843STrond Myklebust 		break;
1899e598d843STrond Myklebust 	case -NFS4ERR_EXPIRED:
1900e598d843STrond Myklebust 		set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
1901e598d843STrond Myklebust 		nfs4_state_start_reclaim_nograce(clp);
19028ba9bf8eSAndy Adamson 		break;
1903c3fad1b1SAndy Adamson 	case -NFS4ERR_BADSESSION:
1904c3fad1b1SAndy Adamson 	case -NFS4ERR_BADSLOT:
1905c3fad1b1SAndy Adamson 	case -NFS4ERR_BAD_HIGH_SLOT:
1906c3fad1b1SAndy Adamson 	case -NFS4ERR_DEADSESSION:
1907c3fad1b1SAndy Adamson 	case -NFS4ERR_SEQ_FALSE_RETRY:
1908c3fad1b1SAndy Adamson 	case -NFS4ERR_SEQ_MISORDERED:
19096df08189SAndy Adamson 		set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
19100b9e2d41SAndy Adamson 		/* Zero session reset errors */
19114f38e4aaSTrond Myklebust 		break;
19127c5d7256STrond Myklebust 	case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
19137c5d7256STrond Myklebust 		set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
19147c5d7256STrond Myklebust 		break;
19154f38e4aaSTrond Myklebust 	default:
1916cc0a9843STrond Myklebust 		dprintk("%s: failed to handle error %d for server %s\n",
1917cc0a9843STrond Myklebust 				__func__, error, clp->cl_hostname);
19184f7cdf18STrond Myklebust 		return error;
1919e598d843STrond Myklebust 	}
1920cc0a9843STrond Myklebust 	dprintk("%s: handled error %d for server %s\n", __func__, error,
1921cc0a9843STrond Myklebust 			clp->cl_hostname);
19224f38e4aaSTrond Myklebust 	return 0;
19234f38e4aaSTrond Myklebust }
1924e598d843STrond Myklebust 
nfs4_do_reclaim(struct nfs_client * clp,const struct nfs4_state_recovery_ops * ops)192502860014STrond Myklebust static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
19261da177e4SLinus Torvalds {
192724d292b8SChuck Lever 	struct nfs4_state_owner *sp;
192824d292b8SChuck Lever 	struct nfs_server *server;
19299f958ab8STrond Myklebust 	struct rb_node *pos;
1930c77e2283STrond Myklebust 	LIST_HEAD(freeme);
19311da177e4SLinus Torvalds 	int status = 0;
19323e2910c7SNeilBrown 	int lost_locks = 0;
19331da177e4SLinus Torvalds 
19347eff03aeSTrond Myklebust restart:
193524d292b8SChuck Lever 	rcu_read_lock();
193624d292b8SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
1937c77e2283STrond Myklebust 		nfs4_purge_state_owners(server, &freeme);
19387eff03aeSTrond Myklebust 		spin_lock(&clp->cl_lock);
193924d292b8SChuck Lever 		for (pos = rb_first(&server->state_owners);
194024d292b8SChuck Lever 		     pos != NULL;
194124d292b8SChuck Lever 		     pos = rb_next(pos)) {
194224d292b8SChuck Lever 			sp = rb_entry(pos,
194324d292b8SChuck Lever 				struct nfs4_state_owner, so_server_node);
194424d292b8SChuck Lever 			if (!test_and_clear_bit(ops->owner_flag_bit,
194524d292b8SChuck Lever 							&sp->so_flags))
19467eff03aeSTrond Myklebust 				continue;
19474a0954efSTrond Myklebust 			if (!atomic_inc_not_zero(&sp->so_count))
19484a0954efSTrond Myklebust 				continue;
19497eff03aeSTrond Myklebust 			spin_unlock(&clp->cl_lock);
195024d292b8SChuck Lever 			rcu_read_unlock();
195124d292b8SChuck Lever 
19523e2910c7SNeilBrown 			status = nfs4_reclaim_open_state(sp, ops, &lost_locks);
19537eff03aeSTrond Myklebust 			if (status < 0) {
19543e2910c7SNeilBrown 				if (lost_locks)
19553e2910c7SNeilBrown 					pr_warn("NFS: %s: lost %d locks\n",
19563e2910c7SNeilBrown 						clp->cl_hostname, lost_locks);
19577eff03aeSTrond Myklebust 				set_bit(ops->owner_flag_bit, &sp->so_flags);
19587eff03aeSTrond Myklebust 				nfs4_put_state_owner(sp);
1959df817ba3STrond Myklebust 				status = nfs4_recovery_handle_error(clp, status);
19602a4a997aSLi Lingfeng 				nfs4_free_state_owners(&freeme);
1961df817ba3STrond Myklebust 				return (status != 0) ? status : -EAGAIN;
196202860014STrond Myklebust 			}
196324d292b8SChuck Lever 
19647eff03aeSTrond Myklebust 			nfs4_put_state_owner(sp);
19657eff03aeSTrond Myklebust 			goto restart;
19667eff03aeSTrond Myklebust 		}
19677eff03aeSTrond Myklebust 		spin_unlock(&clp->cl_lock);
196824d292b8SChuck Lever 	}
196924d292b8SChuck Lever 	rcu_read_unlock();
1970c77e2283STrond Myklebust 	nfs4_free_state_owners(&freeme);
19713e2910c7SNeilBrown 	if (lost_locks)
19723e2910c7SNeilBrown 		pr_warn("NFS: %s: lost %d locks\n",
19733e2910c7SNeilBrown 			clp->cl_hostname, lost_locks);
1974df817ba3STrond Myklebust 	return 0;
19757eff03aeSTrond Myklebust }
19761da177e4SLinus Torvalds 
nfs4_check_lease(struct nfs_client * clp)197702860014STrond Myklebust static int nfs4_check_lease(struct nfs_client *clp)
197802860014STrond Myklebust {
1979a52458b4SNeilBrown 	const struct cred *cred;
1980c48f4f35STrond Myklebust 	const struct nfs4_state_maintenance_ops *ops =
1981c48f4f35STrond Myklebust 		clp->cl_mvops->state_renewal_ops;
19824f38e4aaSTrond Myklebust 	int status;
198302860014STrond Myklebust 
19840f605b56STrond Myklebust 	/* Is the client already known to have an expired lease? */
19850f605b56STrond Myklebust 	if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
19860f605b56STrond Myklebust 		return 0;
1987f15e1e8bSNeilBrown 	cred = ops->get_state_renewal_cred(clp);
19880f605b56STrond Myklebust 	if (cred == NULL) {
198973d8bde5SChuck Lever 		cred = nfs4_get_clid_cred(clp);
19904f38e4aaSTrond Myklebust 		status = -ENOKEY;
19910f605b56STrond Myklebust 		if (cred == NULL)
19920f605b56STrond Myklebust 			goto out;
19930f605b56STrond Myklebust 	}
19948e69514fSBenny Halevy 	status = ops->renew_lease(clp, cred);
1995a52458b4SNeilBrown 	put_cred(cred);
1996bc7a05caSTrond Myklebust 	if (status == -ETIMEDOUT) {
1997bc7a05caSTrond Myklebust 		set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
1998bc7a05caSTrond Myklebust 		return 0;
1999bc7a05caSTrond Myklebust 	}
20000f605b56STrond Myklebust out:
20014f7cdf18STrond Myklebust 	return nfs4_recovery_handle_error(clp, status);
200202860014STrond Myklebust }
200302860014STrond Myklebust 
200447b803c8SAndy Adamson /* Set NFS4CLNT_LEASE_EXPIRED and reclaim reboot state for all v4.0 errors
200547b803c8SAndy Adamson  * and for recoverable errors on EXCHANGE_ID for v4.1
20062a6ee6aaSTrond Myklebust  */
nfs4_handle_reclaim_lease_error(struct nfs_client * clp,int status)20072a6ee6aaSTrond Myklebust static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
20082a6ee6aaSTrond Myklebust {
20092a6ee6aaSTrond Myklebust 	switch (status) {
201089a21736STrond Myklebust 	case -NFS4ERR_SEQ_MISORDERED:
201189a21736STrond Myklebust 		if (test_and_set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state))
201289a21736STrond Myklebust 			return -ESERVERFAULT;
201389a21736STrond Myklebust 		/* Lease confirmation error: retry after purging the lease */
201489a21736STrond Myklebust 		ssleep(1);
201547b803c8SAndy Adamson 		clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
201647b803c8SAndy Adamson 		break;
20172a6ee6aaSTrond Myklebust 	case -NFS4ERR_STALE_CLIENTID:
20182a6ee6aaSTrond Myklebust 		clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
201947b803c8SAndy Adamson 		nfs4_state_start_reclaim_reboot(clp);
20202a6ee6aaSTrond Myklebust 		break;
2021de734831SChuck Lever 	case -NFS4ERR_CLID_INUSE:
2022de734831SChuck Lever 		pr_err("NFS: Server %s reports our clientid is in use\n",
2023de734831SChuck Lever 			clp->cl_hostname);
2024de734831SChuck Lever 		nfs_mark_client_ready(clp, -EPERM);
2025de734831SChuck Lever 		clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
2026de734831SChuck Lever 		return -EPERM;
20272a6ee6aaSTrond Myklebust 	case -EACCES:
20282a6ee6aaSTrond Myklebust 	case -NFS4ERR_DELAY:
20292a6ee6aaSTrond Myklebust 	case -EAGAIN:
20302a6ee6aaSTrond Myklebust 		ssleep(1);
20312a6ee6aaSTrond Myklebust 		break;
20322a6ee6aaSTrond Myklebust 
20332a6ee6aaSTrond Myklebust 	case -NFS4ERR_MINOR_VERS_MISMATCH:
20342a6ee6aaSTrond Myklebust 		if (clp->cl_cons_state == NFS_CS_SESSION_INITING)
20352a6ee6aaSTrond Myklebust 			nfs_mark_client_ready(clp, -EPROTONOSUPPORT);
2036cc0a9843STrond Myklebust 		dprintk("%s: exit with error %d for server %s\n",
2037cc0a9843STrond Myklebust 				__func__, -EPROTONOSUPPORT, clp->cl_hostname);
20382a6ee6aaSTrond Myklebust 		return -EPROTONOSUPPORT;
2039ea027cb2SOlga Kornievskaia 	case -ENOSPC:
2040ea027cb2SOlga Kornievskaia 		if (clp->cl_cons_state == NFS_CS_SESSION_INITING)
2041ea027cb2SOlga Kornievskaia 			nfs_mark_client_ready(clp, -EIO);
2042ea027cb2SOlga Kornievskaia 		return -EIO;
20432a6ee6aaSTrond Myklebust 	case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
20442a6ee6aaSTrond Myklebust 				 * in nfs4_exchange_id */
20452a6ee6aaSTrond Myklebust 	default:
2046cc0a9843STrond Myklebust 		dprintk("%s: exit with error %d for server %s\n", __func__,
2047cc0a9843STrond Myklebust 				status, clp->cl_hostname);
20482a6ee6aaSTrond Myklebust 		return status;
20492a6ee6aaSTrond Myklebust 	}
20502a6ee6aaSTrond Myklebust 	set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
2051cc0a9843STrond Myklebust 	dprintk("%s: handled error %d for server %s\n", __func__, status,
2052cc0a9843STrond Myklebust 			clp->cl_hostname);
20532a6ee6aaSTrond Myklebust 	return 0;
20542a6ee6aaSTrond Myklebust }
20552a6ee6aaSTrond Myklebust 
nfs4_establish_lease(struct nfs_client * clp)2056b42353ffSTrond Myklebust static int nfs4_establish_lease(struct nfs_client *clp)
205702860014STrond Myklebust {
2058a52458b4SNeilBrown 	const struct cred *cred;
2059c48f4f35STrond Myklebust 	const struct nfs4_state_recovery_ops *ops =
2060c48f4f35STrond Myklebust 		clp->cl_mvops->reboot_recovery_ops;
20612a6ee6aaSTrond Myklebust 	int status;
206202860014STrond Myklebust 
20638aafd2fdSTrond Myklebust 	status = nfs4_begin_drain_session(clp);
20648aafd2fdSTrond Myklebust 	if (status != 0)
20658aafd2fdSTrond Myklebust 		return status;
206673d8bde5SChuck Lever 	cred = nfs4_get_clid_cred(clp);
20672a6ee6aaSTrond Myklebust 	if (cred == NULL)
20682a6ee6aaSTrond Myklebust 		return -ENOENT;
2069591d71cbSAndy Adamson 	status = ops->establish_clid(clp, cred);
2070a52458b4SNeilBrown 	put_cred(cred);
20712a6ee6aaSTrond Myklebust 	if (status != 0)
2072b42353ffSTrond Myklebust 		return status;
2073b42353ffSTrond Myklebust 	pnfs_destroy_all_layouts(clp);
2074b42353ffSTrond Myklebust 	return 0;
2075b42353ffSTrond Myklebust }
2076b42353ffSTrond Myklebust 
20776bbb4ae8SChuck Lever /*
20786bbb4ae8SChuck Lever  * Returns zero or a negative errno.  NFS4ERR values are converted
20796bbb4ae8SChuck Lever  * to local errno values.
20806bbb4ae8SChuck Lever  */
nfs4_reclaim_lease(struct nfs_client * clp)2081b42353ffSTrond Myklebust static int nfs4_reclaim_lease(struct nfs_client *clp)
2082b42353ffSTrond Myklebust {
2083b42353ffSTrond Myklebust 	int status;
2084b42353ffSTrond Myklebust 
2085b42353ffSTrond Myklebust 	status = nfs4_establish_lease(clp);
2086b42353ffSTrond Myklebust 	if (status < 0)
20872a6ee6aaSTrond Myklebust 		return nfs4_handle_reclaim_lease_error(clp, status);
2088b42353ffSTrond Myklebust 	if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state))
2089b42353ffSTrond Myklebust 		nfs4_state_start_reclaim_nograce(clp);
2090b42353ffSTrond Myklebust 	if (!test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state))
2091b42353ffSTrond Myklebust 		set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
2092b42353ffSTrond Myklebust 	clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
2093b42353ffSTrond Myklebust 	clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
2094b42353ffSTrond Myklebust 	return 0;
2095b42353ffSTrond Myklebust }
2096b42353ffSTrond Myklebust 
nfs4_purge_lease(struct nfs_client * clp)2097b42353ffSTrond Myklebust static int nfs4_purge_lease(struct nfs_client *clp)
2098b42353ffSTrond Myklebust {
2099b42353ffSTrond Myklebust 	int status;
2100b42353ffSTrond Myklebust 
2101b42353ffSTrond Myklebust 	status = nfs4_establish_lease(clp);
2102b42353ffSTrond Myklebust 	if (status < 0)
2103b42353ffSTrond Myklebust 		return nfs4_handle_reclaim_lease_error(clp, status);
2104b42353ffSTrond Myklebust 	clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
2105b42353ffSTrond Myklebust 	set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
2106b42353ffSTrond Myklebust 	nfs4_state_start_reclaim_nograce(clp);
21072a6ee6aaSTrond Myklebust 	return 0;
210802860014STrond Myklebust }
210902860014STrond Myklebust 
2110c9fdeb28SChuck Lever /*
2111c9fdeb28SChuck Lever  * Try remote migration of one FSID from a source server to a
2112c9fdeb28SChuck Lever  * destination server.  The source server provides a list of
2113c9fdeb28SChuck Lever  * potential destinations.
2114c9fdeb28SChuck Lever  *
2115c9fdeb28SChuck Lever  * Returns zero or a negative NFS4ERR status code.
2116c9fdeb28SChuck Lever  */
nfs4_try_migration(struct nfs_server * server,const struct cred * cred)2117a52458b4SNeilBrown static int nfs4_try_migration(struct nfs_server *server, const struct cred *cred)
2118c9fdeb28SChuck Lever {
2119c9fdeb28SChuck Lever 	struct nfs_client *clp = server->nfs_client;
2120c9fdeb28SChuck Lever 	struct nfs4_fs_locations *locations = NULL;
21210d317bcfSBenjamin Coddington 	struct nfs_fattr *fattr;
2122c9fdeb28SChuck Lever 	struct inode *inode;
2123c9fdeb28SChuck Lever 	struct page *page;
2124c9fdeb28SChuck Lever 	int status, result;
2125c9fdeb28SChuck Lever 
2126c9fdeb28SChuck Lever 	dprintk("--> %s: FSID %llx:%llx on \"%s\"\n", __func__,
2127c9fdeb28SChuck Lever 			(unsigned long long)server->fsid.major,
2128c9fdeb28SChuck Lever 			(unsigned long long)server->fsid.minor,
2129c9fdeb28SChuck Lever 			clp->cl_hostname);
2130c9fdeb28SChuck Lever 
2131c9fdeb28SChuck Lever 	page = alloc_page(GFP_KERNEL);
2132c9fdeb28SChuck Lever 	locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
21330d317bcfSBenjamin Coddington 	fattr = nfs_alloc_fattr();
21340d317bcfSBenjamin Coddington 	if (page == NULL || locations == NULL || fattr == NULL) {
2135c9fdeb28SChuck Lever 		dprintk("<-- %s: no memory\n", __func__);
21360d317bcfSBenjamin Coddington 		result = 0;
2137c3ed2227SBenjamin Coddington 		goto out;
2138c3ed2227SBenjamin Coddington 	}
2139c9fdeb28SChuck Lever 
21400d317bcfSBenjamin Coddington 	locations->fattr = fattr;
21412b0143b5SDavid Howells 	inode = d_inode(server->super->s_root);
21421976b2b3SOlga Kornievskaia 	result = nfs4_proc_get_locations(server, NFS_FH(inode), locations,
21431976b2b3SOlga Kornievskaia 					 page, cred);
2144c9fdeb28SChuck Lever 	if (result) {
2145c9fdeb28SChuck Lever 		dprintk("<-- %s: failed to retrieve fs_locations: %d\n",
2146c9fdeb28SChuck Lever 			__func__, result);
2147c9fdeb28SChuck Lever 		goto out;
2148c9fdeb28SChuck Lever 	}
2149c9fdeb28SChuck Lever 
2150c9fdeb28SChuck Lever 	result = -NFS4ERR_NXIO;
215190e12a31SOlga Kornievskaia 	if (!locations->nlocations)
215290e12a31SOlga Kornievskaia 		goto out;
215390e12a31SOlga Kornievskaia 
2154c3ed2227SBenjamin Coddington 	if (!(locations->fattr->valid & NFS_ATTR_FATTR_V4_LOCATIONS)) {
2155c9fdeb28SChuck Lever 		dprintk("<-- %s: No fs_locations data, migration skipped\n",
2156c9fdeb28SChuck Lever 			__func__);
2157c9fdeb28SChuck Lever 		goto out;
2158c9fdeb28SChuck Lever 	}
2159c9fdeb28SChuck Lever 
21608aafd2fdSTrond Myklebust 	status = nfs4_begin_drain_session(clp);
21611e672e36SWenwen Wang 	if (status != 0) {
21621e672e36SWenwen Wang 		result = status;
21631e672e36SWenwen Wang 		goto out;
21641e672e36SWenwen Wang 	}
2165c9fdeb28SChuck Lever 
2166c9fdeb28SChuck Lever 	status = nfs4_replace_transport(server, locations);
2167c9fdeb28SChuck Lever 	if (status != 0) {
2168c9fdeb28SChuck Lever 		dprintk("<-- %s: failed to replace transport: %d\n",
2169c9fdeb28SChuck Lever 			__func__, status);
2170c9fdeb28SChuck Lever 		goto out;
2171c9fdeb28SChuck Lever 	}
2172c9fdeb28SChuck Lever 
2173c9fdeb28SChuck Lever 	result = 0;
2174c9fdeb28SChuck Lever 	dprintk("<-- %s: migration succeeded\n", __func__);
2175c9fdeb28SChuck Lever 
2176c9fdeb28SChuck Lever out:
2177c9fdeb28SChuck Lever 	if (page != NULL)
2178c9fdeb28SChuck Lever 		__free_page(page);
2179c3ed2227SBenjamin Coddington 	if (locations != NULL)
2180c3ed2227SBenjamin Coddington 		kfree(locations->fattr);
2181c9fdeb28SChuck Lever 	kfree(locations);
2182c9fdeb28SChuck Lever 	if (result) {
2183c9fdeb28SChuck Lever 		pr_err("NFS: migration recovery failed (server %s)\n",
2184c9fdeb28SChuck Lever 				clp->cl_hostname);
2185c9fdeb28SChuck Lever 		set_bit(NFS_MIG_FAILED, &server->mig_status);
2186c9fdeb28SChuck Lever 	}
2187c9fdeb28SChuck Lever 	return result;
2188c9fdeb28SChuck Lever }
2189c9fdeb28SChuck Lever 
2190c9fdeb28SChuck Lever /*
2191c9fdeb28SChuck Lever  * Returns zero or a negative NFS4ERR status code.
2192c9fdeb28SChuck Lever  */
nfs4_handle_migration(struct nfs_client * clp)2193c9fdeb28SChuck Lever static int nfs4_handle_migration(struct nfs_client *clp)
2194c9fdeb28SChuck Lever {
2195c9fdeb28SChuck Lever 	const struct nfs4_state_maintenance_ops *ops =
2196c9fdeb28SChuck Lever 				clp->cl_mvops->state_renewal_ops;
2197c9fdeb28SChuck Lever 	struct nfs_server *server;
2198a52458b4SNeilBrown 	const struct cred *cred;
2199c9fdeb28SChuck Lever 
2200c9fdeb28SChuck Lever 	dprintk("%s: migration reported on \"%s\"\n", __func__,
2201c9fdeb28SChuck Lever 			clp->cl_hostname);
2202c9fdeb28SChuck Lever 
2203f15e1e8bSNeilBrown 	cred = ops->get_state_renewal_cred(clp);
2204c9fdeb28SChuck Lever 	if (cred == NULL)
2205c9fdeb28SChuck Lever 		return -NFS4ERR_NOENT;
2206c9fdeb28SChuck Lever 
2207c9fdeb28SChuck Lever 	clp->cl_mig_gen++;
2208c9fdeb28SChuck Lever restart:
2209c9fdeb28SChuck Lever 	rcu_read_lock();
2210c9fdeb28SChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
2211c9fdeb28SChuck Lever 		int status;
2212c9fdeb28SChuck Lever 
2213c9fdeb28SChuck Lever 		if (server->mig_gen == clp->cl_mig_gen)
2214c9fdeb28SChuck Lever 			continue;
2215c9fdeb28SChuck Lever 		server->mig_gen = clp->cl_mig_gen;
2216c9fdeb28SChuck Lever 
2217c9fdeb28SChuck Lever 		if (!test_and_clear_bit(NFS_MIG_IN_TRANSITION,
2218c9fdeb28SChuck Lever 						&server->mig_status))
2219c9fdeb28SChuck Lever 			continue;
2220c9fdeb28SChuck Lever 
2221c9fdeb28SChuck Lever 		rcu_read_unlock();
2222c9fdeb28SChuck Lever 		status = nfs4_try_migration(server, cred);
2223c9fdeb28SChuck Lever 		if (status < 0) {
2224a52458b4SNeilBrown 			put_cred(cred);
2225c9fdeb28SChuck Lever 			return status;
2226c9fdeb28SChuck Lever 		}
2227c9fdeb28SChuck Lever 		goto restart;
2228c9fdeb28SChuck Lever 	}
2229c9fdeb28SChuck Lever 	rcu_read_unlock();
2230a52458b4SNeilBrown 	put_cred(cred);
2231c9fdeb28SChuck Lever 	return 0;
2232c9fdeb28SChuck Lever }
2233c9fdeb28SChuck Lever 
2234b7f7a66eSChuck Lever /*
2235b7f7a66eSChuck Lever  * Test each nfs_server on the clp's cl_superblocks list to see
2236b7f7a66eSChuck Lever  * if it's moved to another server.  Stop when the server no longer
2237b7f7a66eSChuck Lever  * returns NFS4ERR_LEASE_MOVED.
2238b7f7a66eSChuck Lever  */
nfs4_handle_lease_moved(struct nfs_client * clp)2239b7f7a66eSChuck Lever static int nfs4_handle_lease_moved(struct nfs_client *clp)
2240b7f7a66eSChuck Lever {
2241b7f7a66eSChuck Lever 	const struct nfs4_state_maintenance_ops *ops =
2242b7f7a66eSChuck Lever 				clp->cl_mvops->state_renewal_ops;
2243b7f7a66eSChuck Lever 	struct nfs_server *server;
2244a52458b4SNeilBrown 	const struct cred *cred;
2245b7f7a66eSChuck Lever 
2246b7f7a66eSChuck Lever 	dprintk("%s: lease moved reported on \"%s\"\n", __func__,
2247b7f7a66eSChuck Lever 			clp->cl_hostname);
2248b7f7a66eSChuck Lever 
2249f15e1e8bSNeilBrown 	cred = ops->get_state_renewal_cred(clp);
2250b7f7a66eSChuck Lever 	if (cred == NULL)
2251b7f7a66eSChuck Lever 		return -NFS4ERR_NOENT;
2252b7f7a66eSChuck Lever 
2253b7f7a66eSChuck Lever 	clp->cl_mig_gen++;
2254b7f7a66eSChuck Lever restart:
2255b7f7a66eSChuck Lever 	rcu_read_lock();
2256b7f7a66eSChuck Lever 	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
2257b7f7a66eSChuck Lever 		struct inode *inode;
2258b7f7a66eSChuck Lever 		int status;
2259b7f7a66eSChuck Lever 
2260b7f7a66eSChuck Lever 		if (server->mig_gen == clp->cl_mig_gen)
2261b7f7a66eSChuck Lever 			continue;
2262b7f7a66eSChuck Lever 		server->mig_gen = clp->cl_mig_gen;
2263b7f7a66eSChuck Lever 
2264b7f7a66eSChuck Lever 		rcu_read_unlock();
2265b7f7a66eSChuck Lever 
22662b0143b5SDavid Howells 		inode = d_inode(server->super->s_root);
2267b7f7a66eSChuck Lever 		status = nfs4_proc_fsid_present(inode, cred);
2268b7f7a66eSChuck Lever 		if (status != -NFS4ERR_MOVED)
2269b7f7a66eSChuck Lever 			goto restart;	/* wasn't this one */
2270b7f7a66eSChuck Lever 		if (nfs4_try_migration(server, cred) == -NFS4ERR_LEASE_MOVED)
2271b7f7a66eSChuck Lever 			goto restart;	/* there are more */
2272b7f7a66eSChuck Lever 		goto out;
2273b7f7a66eSChuck Lever 	}
2274b7f7a66eSChuck Lever 	rcu_read_unlock();
2275b7f7a66eSChuck Lever 
2276b7f7a66eSChuck Lever out:
2277a52458b4SNeilBrown 	put_cred(cred);
2278b7f7a66eSChuck Lever 	return 0;
2279b7f7a66eSChuck Lever }
2280b7f7a66eSChuck Lever 
228105f4c350SChuck Lever /**
228205f4c350SChuck Lever  * nfs4_discover_server_trunking - Detect server IP address trunking
228305f4c350SChuck Lever  *
228405f4c350SChuck Lever  * @clp: nfs_client under test
228505f4c350SChuck Lever  * @result: OUT: found nfs_client, or clp
228605f4c350SChuck Lever  *
228705f4c350SChuck Lever  * Returns zero or a negative errno.  If zero is returned,
228805f4c350SChuck Lever  * an nfs_client pointer is planted in "result".
228905f4c350SChuck Lever  *
229005f4c350SChuck Lever  * Note: since we are invoked in process context, and
229105f4c350SChuck Lever  * not from inside the state manager, we cannot use
229205f4c350SChuck Lever  * nfs4_handle_reclaim_lease_error().
229305f4c350SChuck Lever  */
nfs4_discover_server_trunking(struct nfs_client * clp,struct nfs_client ** result)229405f4c350SChuck Lever int nfs4_discover_server_trunking(struct nfs_client *clp,
229505f4c350SChuck Lever 				  struct nfs_client **result)
229605f4c350SChuck Lever {
229705f4c350SChuck Lever 	const struct nfs4_state_recovery_ops *ops =
229805f4c350SChuck Lever 				clp->cl_mvops->reboot_recovery_ops;
229905f4c350SChuck Lever 	struct rpc_clnt *clnt;
2300a52458b4SNeilBrown 	const struct cred *cred;
23014edaa308SChuck Lever 	int i, status;
230205f4c350SChuck Lever 
230305f4c350SChuck Lever 	dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname);
230405f4c350SChuck Lever 
230505f4c350SChuck Lever 	clnt = clp->cl_rpcclient;
230605f4c350SChuck Lever 	i = 0;
230705f4c350SChuck Lever 
230805f4c350SChuck Lever 	mutex_lock(&nfs_clid_init_mutex);
230905f4c350SChuck Lever again:
2310ea33e6c3STrond Myklebust 	status  = -ENOENT;
231173d8bde5SChuck Lever 	cred = nfs4_get_clid_cred(clp);
231205f4c350SChuck Lever 	if (cred == NULL)
231305f4c350SChuck Lever 		goto out_unlock;
231405f4c350SChuck Lever 
231505f4c350SChuck Lever 	status = ops->detect_trunking(clp, result, cred);
2316a52458b4SNeilBrown 	put_cred(cred);
231705f4c350SChuck Lever 	switch (status) {
231805f4c350SChuck Lever 	case 0:
2319898fc11bSTrond Myklebust 	case -EINTR:
2320898fc11bSTrond Myklebust 	case -ERESTARTSYS:
232105f4c350SChuck Lever 		break;
232205f4c350SChuck Lever 	case -ETIMEDOUT:
2323150e7260STrond Myklebust 		if (clnt->cl_softrtry)
2324150e7260STrond Myklebust 			break;
2325df561f66SGustavo A. R. Silva 		fallthrough;
2326150e7260STrond Myklebust 	case -NFS4ERR_DELAY:
232705f4c350SChuck Lever 	case -EAGAIN:
232805f4c350SChuck Lever 		ssleep(1);
2329df561f66SGustavo A. R. Silva 		fallthrough;
2330202c312dSTrond Myklebust 	case -NFS4ERR_STALE_CLIENTID:
233105f4c350SChuck Lever 		dprintk("NFS: %s after status %d, retrying\n",
233205f4c350SChuck Lever 			__func__, status);
233305f4c350SChuck Lever 		goto again;
23344edaa308SChuck Lever 	case -EACCES:
2335d688f7b8SChuck Lever 		if (i++ == 0) {
2336d688f7b8SChuck Lever 			nfs4_root_machine_cred(clp);
2337d688f7b8SChuck Lever 			goto again;
2338d688f7b8SChuck Lever 		}
23396d769f1eSJeff Layton 		if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX)
23404edaa308SChuck Lever 			break;
2341df561f66SGustavo A. R. Silva 		fallthrough;
234205f4c350SChuck Lever 	case -NFS4ERR_CLID_INUSE:
234305f4c350SChuck Lever 	case -NFS4ERR_WRONGSEC:
23446d769f1eSJeff Layton 		/* No point in retrying if we already used RPC_AUTH_UNIX */
23456d769f1eSJeff Layton 		if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX) {
23466d769f1eSJeff Layton 			status = -EPERM;
23476d769f1eSJeff Layton 			break;
23486d769f1eSJeff Layton 		}
234979d852bfSChuck Lever 		clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX);
235005f4c350SChuck Lever 		if (IS_ERR(clnt)) {
235105f4c350SChuck Lever 			status = PTR_ERR(clnt);
235205f4c350SChuck Lever 			break;
235305f4c350SChuck Lever 		}
2354b193d59aSTrond Myklebust 		/* Note: this is safe because we haven't yet marked the
2355b193d59aSTrond Myklebust 		 * client as ready, so we are the only user of
2356b193d59aSTrond Myklebust 		 * clp->cl_rpcclient
2357b193d59aSTrond Myklebust 		 */
2358b193d59aSTrond Myklebust 		clnt = xchg(&clp->cl_rpcclient, clnt);
2359b193d59aSTrond Myklebust 		rpc_shutdown_client(clnt);
2360b193d59aSTrond Myklebust 		clnt = clp->cl_rpcclient;
236105f4c350SChuck Lever 		goto again;
236205f4c350SChuck Lever 
236305f4c350SChuck Lever 	case -NFS4ERR_MINOR_VERS_MISMATCH:
236405f4c350SChuck Lever 		status = -EPROTONOSUPPORT;
236505f4c350SChuck Lever 		break;
236605f4c350SChuck Lever 
236705f4c350SChuck Lever 	case -EKEYEXPIRED:
236805f4c350SChuck Lever 	case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
236905f4c350SChuck Lever 				 * in nfs4_exchange_id */
237005f4c350SChuck Lever 		status = -EKEYEXPIRED;
2371ea33e6c3STrond Myklebust 		break;
2372ea33e6c3STrond Myklebust 	default:
2373ea33e6c3STrond Myklebust 		pr_warn("NFS: %s unhandled error %d. Exiting with error EIO\n",
2374ea33e6c3STrond Myklebust 				__func__, status);
2375ea33e6c3STrond Myklebust 		status = -EIO;
237605f4c350SChuck Lever 	}
237705f4c350SChuck Lever 
237805f4c350SChuck Lever out_unlock:
237905f4c350SChuck Lever 	mutex_unlock(&nfs_clid_init_mutex);
238005f4c350SChuck Lever 	dprintk("NFS: %s: status = %d\n", __func__, status);
238105f4c350SChuck Lever 	return status;
238205f4c350SChuck Lever }
238305f4c350SChuck Lever 
238476db6d95SAndy Adamson #ifdef CONFIG_NFS_V4_1
nfs4_schedule_session_recovery(struct nfs4_session * session,int err)23859f594791STrond Myklebust void nfs4_schedule_session_recovery(struct nfs4_session *session, int err)
23860400a6b0STrond Myklebust {
2387444f72feSTrond Myklebust 	struct nfs_client *clp = session->clp;
2388444f72feSTrond Myklebust 
23899f594791STrond Myklebust 	switch (err) {
23909f594791STrond Myklebust 	default:
2391444f72feSTrond Myklebust 		set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
23929f594791STrond Myklebust 		break;
23939f594791STrond Myklebust 	case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
23949f594791STrond Myklebust 		set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
23959f594791STrond Myklebust 	}
2396d94cbf6cSTrond Myklebust 	nfs4_schedule_state_manager(clp);
23970400a6b0STrond Myklebust }
2398cbdabc7fSAndy Adamson EXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery);
23990400a6b0STrond Myklebust 
nfs41_notify_server(struct nfs_client * clp)24003f10a6afSAnna Schumaker void nfs41_notify_server(struct nfs_client *clp)
2401ac074835STrond Myklebust {
2402ac074835STrond Myklebust 	/* Use CHECK_LEASE to ping the server with a SEQUENCE */
2403ac074835STrond Myklebust 	set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
2404ac074835STrond Myklebust 	nfs4_schedule_state_manager(clp);
2405ac074835STrond Myklebust }
2406ac074835STrond Myklebust 
nfs4_reset_all_state(struct nfs_client * clp)24070f79fd6fSTrond Myklebust static void nfs4_reset_all_state(struct nfs_client *clp)
24080f79fd6fSTrond Myklebust {
24090f79fd6fSTrond Myklebust 	if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
24102c820d9aSChuck Lever 		set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
2411be0bfed0STrond Myklebust 		clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
24120f79fd6fSTrond Myklebust 		nfs4_state_start_reclaim_nograce(clp);
2413cc0a9843STrond Myklebust 		dprintk("%s: scheduling reset of all state for server %s!\n",
2414cc0a9843STrond Myklebust 				__func__, clp->cl_hostname);
24150400a6b0STrond Myklebust 		nfs4_schedule_state_manager(clp);
24160f79fd6fSTrond Myklebust 	}
24170f79fd6fSTrond Myklebust }
24180f79fd6fSTrond Myklebust 
nfs41_handle_server_reboot(struct nfs_client * clp)24190f79fd6fSTrond Myklebust static void nfs41_handle_server_reboot(struct nfs_client *clp)
24200f79fd6fSTrond Myklebust {
24210f79fd6fSTrond Myklebust 	if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
24220f79fd6fSTrond Myklebust 		nfs4_state_start_reclaim_reboot(clp);
2423cc0a9843STrond Myklebust 		dprintk("%s: server %s rebooted!\n", __func__,
2424cc0a9843STrond Myklebust 				clp->cl_hostname);
24250400a6b0STrond Myklebust 		nfs4_schedule_state_manager(clp);
24260f79fd6fSTrond Myklebust 	}
24270f79fd6fSTrond Myklebust }
24280f79fd6fSTrond Myklebust 
nfs41_handle_all_state_revoked(struct nfs_client * clp)24298b895ce6STrond Myklebust static void nfs41_handle_all_state_revoked(struct nfs_client *clp)
24300f79fd6fSTrond Myklebust {
24310f79fd6fSTrond Myklebust 	nfs4_reset_all_state(clp);
2432cc0a9843STrond Myklebust 	dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname);
24330f79fd6fSTrond Myklebust }
24340f79fd6fSTrond Myklebust 
nfs41_handle_some_state_revoked(struct nfs_client * clp)24358b895ce6STrond Myklebust static void nfs41_handle_some_state_revoked(struct nfs_client *clp)
24368b895ce6STrond Myklebust {
243745870d69STrond Myklebust 	nfs4_state_start_reclaim_nograce(clp);
24388b895ce6STrond Myklebust 	nfs4_schedule_state_manager(clp);
24398b895ce6STrond Myklebust 
24408b895ce6STrond Myklebust 	dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname);
24418b895ce6STrond Myklebust }
24428b895ce6STrond Myklebust 
nfs41_handle_recallable_state_revoked(struct nfs_client * clp)24430f79fd6fSTrond Myklebust static void nfs41_handle_recallable_state_revoked(struct nfs_client *clp)
24440f79fd6fSTrond Myklebust {
24454099287fSTrond Myklebust 	/* FIXME: For now, we destroy all layouts. */
24464099287fSTrond Myklebust 	pnfs_destroy_all_layouts(clp);
24478ca017c8SScott Mayhew 	nfs_test_expired_all_delegations(clp);
2448cc0a9843STrond Myklebust 	dprintk("%s: Recallable state revoked on server %s!\n", __func__,
2449cc0a9843STrond Myklebust 			clp->cl_hostname);
24500f79fd6fSTrond Myklebust }
24510f79fd6fSTrond Myklebust 
nfs41_handle_backchannel_fault(struct nfs_client * clp)2452a9e64442SWeston Andros Adamson static void nfs41_handle_backchannel_fault(struct nfs_client *clp)
24530f79fd6fSTrond Myklebust {
2454b1352905STrond Myklebust 	set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
24550400a6b0STrond Myklebust 	nfs4_schedule_state_manager(clp);
2456b1352905STrond Myklebust 
2457cc0a9843STrond Myklebust 	dprintk("%s: server %s declared a backchannel fault\n", __func__,
2458cc0a9843STrond Myklebust 			clp->cl_hostname);
24590f79fd6fSTrond Myklebust }
24600f79fd6fSTrond Myklebust 
nfs41_handle_cb_path_down(struct nfs_client * clp)2461a9e64442SWeston Andros Adamson static void nfs41_handle_cb_path_down(struct nfs_client *clp)
2462a9e64442SWeston Andros Adamson {
2463a9e64442SWeston Andros Adamson 	if (test_and_set_bit(NFS4CLNT_BIND_CONN_TO_SESSION,
2464a9e64442SWeston Andros Adamson 		&clp->cl_state) == 0)
2465a9e64442SWeston Andros Adamson 		nfs4_schedule_state_manager(clp);
2466a9e64442SWeston Andros Adamson }
2467a9e64442SWeston Andros Adamson 
nfs41_handle_sequence_flag_errors(struct nfs_client * clp,u32 flags,bool recovery)24680a014a44STrond Myklebust void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags,
24690a014a44STrond Myklebust 		bool recovery)
24700629e370SAlexandros Batsakis {
24710629e370SAlexandros Batsakis 	if (!flags)
24720629e370SAlexandros Batsakis 		return;
24732c820d9aSChuck Lever 
24742c820d9aSChuck Lever 	dprintk("%s: \"%s\" (client ID %llx) flags=0x%08x\n",
24752c820d9aSChuck Lever 		__func__, clp->cl_hostname, clp->cl_clientid, flags);
24760a014a44STrond Myklebust 	/*
24770a014a44STrond Myklebust 	 * If we're called from the state manager thread, then assume we're
24780a014a44STrond Myklebust 	 * already handling the RECLAIM_NEEDED and/or STATE_REVOKED.
24790a014a44STrond Myklebust 	 * Those flags are expected to remain set until we're done
24800a014a44STrond Myklebust 	 * recovering (see RFC5661, section 18.46.3).
24810a014a44STrond Myklebust 	 */
24820a014a44STrond Myklebust 	if (recovery)
24830a014a44STrond Myklebust 		goto out_recovery;
24842c820d9aSChuck Lever 
2485111d489fSTrond Myklebust 	if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
24860f79fd6fSTrond Myklebust 		nfs41_handle_server_reboot(clp);
24878b895ce6STrond Myklebust 	if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED))
24888b895ce6STrond Myklebust 		nfs41_handle_all_state_revoked(clp);
24898b895ce6STrond Myklebust 	if (flags & (SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED |
2490d1c2331eSChuck Lever 			    SEQ4_STATUS_ADMIN_STATE_REVOKED))
24918b895ce6STrond Myklebust 		nfs41_handle_some_state_revoked(clp);
2492d1c2331eSChuck Lever 	if (flags & SEQ4_STATUS_LEASE_MOVED)
2493d1c2331eSChuck Lever 		nfs4_schedule_lease_moved_recovery(clp);
2494111d489fSTrond Myklebust 	if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
24950f79fd6fSTrond Myklebust 		nfs41_handle_recallable_state_revoked(clp);
24960a014a44STrond Myklebust out_recovery:
2497a9e64442SWeston Andros Adamson 	if (flags & SEQ4_STATUS_BACKCHANNEL_FAULT)
2498a9e64442SWeston Andros Adamson 		nfs41_handle_backchannel_fault(clp);
2499a9e64442SWeston Andros Adamson 	else if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
25000629e370SAlexandros Batsakis 				SEQ4_STATUS_CB_PATH_DOWN_SESSION))
25010f79fd6fSTrond Myklebust 		nfs41_handle_cb_path_down(clp);
25020629e370SAlexandros Batsakis }
25030629e370SAlexandros Batsakis 
nfs4_reset_session(struct nfs_client * clp)2504c3fad1b1SAndy Adamson static int nfs4_reset_session(struct nfs_client *clp)
2505c3fad1b1SAndy Adamson {
2506a52458b4SNeilBrown 	const struct cred *cred;
2507c3fad1b1SAndy Adamson 	int status;
2508c3fad1b1SAndy Adamson 
25091a47e7a6STrond Myklebust 	if (!nfs4_has_session(clp))
25101a47e7a6STrond Myklebust 		return 0;
25118aafd2fdSTrond Myklebust 	status = nfs4_begin_drain_session(clp);
25128aafd2fdSTrond Myklebust 	if (status != 0)
25138aafd2fdSTrond Myklebust 		return status;
251473d8bde5SChuck Lever 	cred = nfs4_get_clid_cred(clp);
2515848f5bdaSTrond Myklebust 	status = nfs4_proc_destroy_session(clp->cl_session, cred);
2516c489ee29STrond Myklebust 	switch (status) {
2517c489ee29STrond Myklebust 	case 0:
2518c489ee29STrond Myklebust 	case -NFS4ERR_BADSESSION:
2519c489ee29STrond Myklebust 	case -NFS4ERR_DEADSESSION:
2520c489ee29STrond Myklebust 		break;
2521c489ee29STrond Myklebust 	case -NFS4ERR_BACK_CHAN_BUSY:
2522c489ee29STrond Myklebust 	case -NFS4ERR_DELAY:
2523c489ee29STrond Myklebust 		set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
2524c489ee29STrond Myklebust 		status = 0;
2525c489ee29STrond Myklebust 		ssleep(1);
2526c489ee29STrond Myklebust 		goto out;
2527c489ee29STrond Myklebust 	default:
2528f455848aSRicardo Labiaga 		status = nfs4_recovery_handle_error(clp, status);
2529c3fad1b1SAndy Adamson 		goto out;
2530c3fad1b1SAndy Adamson 	}
2531c3fad1b1SAndy Adamson 
2532c3fad1b1SAndy Adamson 	memset(clp->cl_session->sess_id.data, 0, NFS4_MAX_SESSIONID_LEN);
2533848f5bdaSTrond Myklebust 	status = nfs4_proc_create_session(clp, cred);
253441f54a55SAndy Adamson 	if (status) {
2535cc0a9843STrond Myklebust 		dprintk("%s: session reset failed with status %d for server %s!\n",
2536cc0a9843STrond Myklebust 			__func__, status, clp->cl_hostname);
2537f2c1b510STrond Myklebust 		status = nfs4_handle_reclaim_lease_error(clp, status);
253841f54a55SAndy Adamson 		goto out;
253941f54a55SAndy Adamson 	}
2540bda197f5STrond Myklebust 	nfs41_finish_session_reset(clp);
2541cc0a9843STrond Myklebust 	dprintk("%s: session reset was successful for server %s!\n",
2542cc0a9843STrond Myklebust 			__func__, clp->cl_hostname);
254341f54a55SAndy Adamson out:
2544a52458b4SNeilBrown 	put_cred(cred);
2545c3fad1b1SAndy Adamson 	return status;
2546c3fad1b1SAndy Adamson }
254776db6d95SAndy Adamson 
nfs4_bind_conn_to_session(struct nfs_client * clp)2548a9e64442SWeston Andros Adamson static int nfs4_bind_conn_to_session(struct nfs_client *clp)
2549a9e64442SWeston Andros Adamson {
2550a52458b4SNeilBrown 	const struct cred *cred;
25512cf047c9STrond Myklebust 	int ret;
25522cf047c9STrond Myklebust 
25531a47e7a6STrond Myklebust 	if (!nfs4_has_session(clp))
25541a47e7a6STrond Myklebust 		return 0;
25558aafd2fdSTrond Myklebust 	ret = nfs4_begin_drain_session(clp);
25568aafd2fdSTrond Myklebust 	if (ret != 0)
25578aafd2fdSTrond Myklebust 		return ret;
255873d8bde5SChuck Lever 	cred = nfs4_get_clid_cred(clp);
25592cf047c9STrond Myklebust 	ret = nfs4_proc_bind_conn_to_session(clp, cred);
2560a52458b4SNeilBrown 	put_cred(cred);
256143ac544cSTrond Myklebust 	clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
2562bf674c82STrond Myklebust 	switch (ret) {
2563bf674c82STrond Myklebust 	case 0:
2564cc0a9843STrond Myklebust 		dprintk("%s: bind_conn_to_session was successful for server %s!\n",
2565cc0a9843STrond Myklebust 			__func__, clp->cl_hostname);
2566bf674c82STrond Myklebust 		break;
2567bf674c82STrond Myklebust 	case -NFS4ERR_DELAY:
2568bf674c82STrond Myklebust 		ssleep(1);
2569bf674c82STrond Myklebust 		set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
2570bf674c82STrond Myklebust 		break;
2571bf674c82STrond Myklebust 	default:
2572bf674c82STrond Myklebust 		return nfs4_recovery_handle_error(clp, ret);
2573bf674c82STrond Myklebust 	}
2574bf674c82STrond Myklebust 	return 0;
2575a9e64442SWeston Andros Adamson }
2576b5fdf841STrond Myklebust 
nfs4_layoutreturn_any_run(struct nfs_client * clp)2577b5fdf841STrond Myklebust static void nfs4_layoutreturn_any_run(struct nfs_client *clp)
2578b5fdf841STrond Myklebust {
2579b5fdf841STrond Myklebust 	int iomode = 0;
2580b5fdf841STrond Myklebust 
2581b5fdf841STrond Myklebust 	if (test_and_clear_bit(NFS4CLNT_RECALL_ANY_LAYOUT_READ, &clp->cl_state))
2582b5fdf841STrond Myklebust 		iomode += IOMODE_READ;
2583b5fdf841STrond Myklebust 	if (test_and_clear_bit(NFS4CLNT_RECALL_ANY_LAYOUT_RW, &clp->cl_state))
2584b5fdf841STrond Myklebust 		iomode += IOMODE_RW;
2585b5fdf841STrond Myklebust 	/* Note: IOMODE_READ + IOMODE_RW == IOMODE_ANY */
2586b5fdf841STrond Myklebust 	if (iomode) {
2587b5fdf841STrond Myklebust 		pnfs_layout_return_unused_byclid(clp, iomode);
2588b5fdf841STrond Myklebust 		set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
2589b5fdf841STrond Myklebust 	}
2590b5fdf841STrond Myklebust }
259176db6d95SAndy Adamson #else /* CONFIG_NFS_V4_1 */
nfs4_reset_session(struct nfs_client * clp)2592c3fad1b1SAndy Adamson static int nfs4_reset_session(struct nfs_client *clp) { return 0; }
2593a9e64442SWeston Andros Adamson 
nfs4_bind_conn_to_session(struct nfs_client * clp)2594a9e64442SWeston Andros Adamson static int nfs4_bind_conn_to_session(struct nfs_client *clp)
2595a9e64442SWeston Andros Adamson {
2596a9e64442SWeston Andros Adamson 	return 0;
2597a9e64442SWeston Andros Adamson }
2598b5fdf841STrond Myklebust 
nfs4_layoutreturn_any_run(struct nfs_client * clp)2599b5fdf841STrond Myklebust static void nfs4_layoutreturn_any_run(struct nfs_client *clp)
2600b5fdf841STrond Myklebust {
2601b5fdf841STrond Myklebust }
260276db6d95SAndy Adamson #endif /* CONFIG_NFS_V4_1 */
260376db6d95SAndy Adamson 
nfs4_state_manager(struct nfs_client * clp)2604e005e804STrond Myklebust static void nfs4_state_manager(struct nfs_client *clp)
260502860014STrond Myklebust {
26063e17898aSTrond Myklebust 	unsigned int memflags;
260702860014STrond Myklebust 	int status = 0;
26088ed27d4fSWeston Andros Adamson 	const char *section = "", *section_sep = "";
260902860014STrond Myklebust 
26103e17898aSTrond Myklebust 	/*
26113e17898aSTrond Myklebust 	 * State recovery can deadlock if the direct reclaim code tries
26123e17898aSTrond Myklebust 	 * start NFS writeback. So ensure memory allocations are all
26133e17898aSTrond Myklebust 	 * GFP_NOFS.
26143e17898aSTrond Myklebust 	 */
26153e17898aSTrond Myklebust 	memflags = memalloc_nofs_save();
26163e17898aSTrond Myklebust 
261702860014STrond Myklebust 	/* Ensure exclusive access to NFSv4 state */
261847c2199bSTrond Myklebust 	do {
2619511ba52eSChuck Lever 		trace_nfs4_state_mgr(clp);
2620aeabb3c9STrond Myklebust 		clear_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
26212c820d9aSChuck Lever 		if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
26228ed27d4fSWeston Andros Adamson 			section = "purge state";
2623b42353ffSTrond Myklebust 			status = nfs4_purge_lease(clp);
26242a6ee6aaSTrond Myklebust 			if (status < 0)
26252a6ee6aaSTrond Myklebust 				goto out_error;
2626b42353ffSTrond Myklebust 			continue;
26272c820d9aSChuck Lever 		}
26282c820d9aSChuck Lever 
2629b42353ffSTrond Myklebust 		if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
26308ed27d4fSWeston Andros Adamson 			section = "lease expired";
2631286d7d6aSTrond Myklebust 			/* We're going to have to re-establish a clientid */
263202860014STrond Myklebust 			status = nfs4_reclaim_lease(clp);
26332a6ee6aaSTrond Myklebust 			if (status < 0)
26341da177e4SLinus Torvalds 				goto out_error;
26352a6ee6aaSTrond Myklebust 			continue;
2636e598d843STrond Myklebust 		}
2637e598d843STrond Myklebust 
2638c3fad1b1SAndy Adamson 		/* Initialize or reset the session */
26391a47e7a6STrond Myklebust 		if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) {
26408ed27d4fSWeston Andros Adamson 			section = "reset session";
2641c3fad1b1SAndy Adamson 			status = nfs4_reset_session(clp);
2642b6d408baSTrond Myklebust 			if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
264376db6d95SAndy Adamson 				continue;
2644b6d408baSTrond Myklebust 			if (status < 0)
264576db6d95SAndy Adamson 				goto out_error;
264676db6d95SAndy Adamson 		}
2647b6d408baSTrond Myklebust 
2648a9e64442SWeston Andros Adamson 		/* Send BIND_CONN_TO_SESSION */
2649a9e64442SWeston Andros Adamson 		if (test_and_clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION,
26501a47e7a6STrond Myklebust 				&clp->cl_state)) {
26518ed27d4fSWeston Andros Adamson 			section = "bind conn to session";
2652a9e64442SWeston Andros Adamson 			status = nfs4_bind_conn_to_session(clp);
2653a9e64442SWeston Andros Adamson 			if (status < 0)
2654a9e64442SWeston Andros Adamson 				goto out_error;
2655bf674c82STrond Myklebust 			continue;
2656a9e64442SWeston Andros Adamson 		}
2657a9e64442SWeston Andros Adamson 
26585df904aeSTrond Myklebust 		if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
26595df904aeSTrond Myklebust 			section = "check lease";
26605df904aeSTrond Myklebust 			status = nfs4_check_lease(clp);
26615df904aeSTrond Myklebust 			if (status < 0)
26625df904aeSTrond Myklebust 				goto out_error;
26638faaa6d5SOlga Kornievskaia 			continue;
2664c9fdeb28SChuck Lever 		}
2665c9fdeb28SChuck Lever 
2666c9fdeb28SChuck Lever 		if (test_and_clear_bit(NFS4CLNT_MOVED, &clp->cl_state)) {
2667c9fdeb28SChuck Lever 			section = "migration";
2668c9fdeb28SChuck Lever 			status = nfs4_handle_migration(clp);
2669c9fdeb28SChuck Lever 			if (status < 0)
2670c9fdeb28SChuck Lever 				goto out_error;
26715df904aeSTrond Myklebust 		}
26725df904aeSTrond Myklebust 
2673b7f7a66eSChuck Lever 		if (test_and_clear_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state)) {
2674b7f7a66eSChuck Lever 			section = "lease moved";
2675b7f7a66eSChuck Lever 			status = nfs4_handle_lease_moved(clp);
2676b7f7a66eSChuck Lever 			if (status < 0)
2677b7f7a66eSChuck Lever 				goto out_error;
2678b7f7a66eSChuck Lever 		}
2679b7f7a66eSChuck Lever 
2680b79a4a1bSTrond Myklebust 		/* First recover reboot state... */
2681e345e88aSTrond Myklebust 		if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
26828ed27d4fSWeston Andros Adamson 			section = "reclaim reboot";
2683591d71cbSAndy Adamson 			status = nfs4_do_reclaim(clp,
2684c48f4f35STrond Myklebust 				clp->cl_mvops->reboot_recovery_ops);
2685df817ba3STrond Myklebust 			if (status == -EAGAIN)
2686b79a4a1bSTrond Myklebust 				continue;
2687b6d408baSTrond Myklebust 			if (status < 0)
2688b6d408baSTrond Myklebust 				goto out_error;
2689df817ba3STrond Myklebust 			nfs4_state_end_reclaim_reboot(clp);
26905d917cbaSTrond Myklebust 			continue;
2691b79a4a1bSTrond Myklebust 		}
2692b79a4a1bSTrond Myklebust 
269345870d69STrond Myklebust 		/* Detect expired delegations... */
269445870d69STrond Myklebust 		if (test_and_clear_bit(NFS4CLNT_DELEGATION_EXPIRED, &clp->cl_state)) {
269545870d69STrond Myklebust 			section = "detect expired delegations";
269645870d69STrond Myklebust 			nfs_reap_expired_delegations(clp);
269745870d69STrond Myklebust 			continue;
269845870d69STrond Myklebust 		}
269945870d69STrond Myklebust 
2700b79a4a1bSTrond Myklebust 		/* Now recover expired state... */
270167e7b52dSTrond Myklebust 		if (test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
27028ed27d4fSWeston Andros Adamson 			section = "reclaim nograce";
2703591d71cbSAndy Adamson 			status = nfs4_do_reclaim(clp,
2704c48f4f35STrond Myklebust 				clp->cl_mvops->nograce_recovery_ops);
2705df817ba3STrond Myklebust 			if (status == -EAGAIN)
270602860014STrond Myklebust 				continue;
2707b6d408baSTrond Myklebust 			if (status < 0)
2708b79a4a1bSTrond Myklebust 				goto out_error;
270967e7b52dSTrond Myklebust 			clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
27101da177e4SLinus Torvalds 		}
2711707fb4b3STrond Myklebust 
27123e17898aSTrond Myklebust 		memalloc_nofs_restore(memflags);
27135601a00dSAlexandros Batsakis 		nfs4_end_drain_session(clp);
2714aeabb3c9STrond Myklebust 		nfs4_clear_state_manager_bit(clp);
2715aeabb3c9STrond Myklebust 
2716ed1cc05aSTrond Myklebust 		if (test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) &&
2717ed1cc05aSTrond Myklebust 		    !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING,
2718ed1cc05aSTrond Myklebust 				      &clp->cl_state)) {
2719ed1cc05aSTrond Myklebust 			memflags = memalloc_nofs_save();
2720ed1cc05aSTrond Myklebust 			continue;
2721ed1cc05aSTrond Myklebust 		}
2722ed1cc05aSTrond Myklebust 
2723b5fdf841STrond Myklebust 		if (!test_and_set_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state)) {
2724707fb4b3STrond Myklebust 			if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
2725707fb4b3STrond Myklebust 				nfs_client_return_marked_delegations(clp);
2726aeabb3c9STrond Myklebust 				set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
2727aeabb3c9STrond Myklebust 			}
2728b5fdf841STrond Myklebust 			nfs4_layoutreturn_any_run(clp);
2729b5fdf841STrond Myklebust 			clear_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state);
2730707fb4b3STrond Myklebust 		}
2731e005e804STrond Myklebust 
2732e005e804STrond Myklebust 		return;
27334dc73c67SNeilBrown 
2734a1aa09beSTrond Myklebust 	} while (refcount_read(&clp->cl_count) > 1 && !signalled());
273521a446cfSTrond Myklebust 	goto out_drain;
273621a446cfSTrond Myklebust 
27371da177e4SLinus Torvalds out_error:
27388ed27d4fSWeston Andros Adamson 	if (strlen(section))
27398ed27d4fSWeston Andros Adamson 		section_sep = ": ";
2740511ba52eSChuck Lever 	trace_nfs4_state_mgr_failed(clp, section, status);
27418ed27d4fSWeston Andros Adamson 	pr_warn_ratelimited("NFS: state manager%s%s failed on NFSv4 server %s"
27428ed27d4fSWeston Andros Adamson 			" with error %d\n", section_sep, section,
27438ed27d4fSWeston Andros Adamson 			clp->cl_hostname, -status);
2744ffe5a830SChuck Lever 	ssleep(1);
274521a446cfSTrond Myklebust out_drain:
27463e17898aSTrond Myklebust 	memalloc_nofs_restore(memflags);
27475601a00dSAlexandros Batsakis 	nfs4_end_drain_session(clp);
2748e005e804STrond Myklebust 	nfs4_clear_state_manager_bit(clp);
2749e005e804STrond Myklebust }
2750e005e804STrond Myklebust 
nfs4_run_state_manager(void * ptr)2751e005e804STrond Myklebust static int nfs4_run_state_manager(void *ptr)
2752e005e804STrond Myklebust {
2753e005e804STrond Myklebust 	struct nfs_client *clp = ptr;
27544dc73c67SNeilBrown 	struct rpc_clnt *cl = clp->cl_rpcclient;
27554dc73c67SNeilBrown 
27564dc73c67SNeilBrown 	while (cl != cl->cl_parent)
27574dc73c67SNeilBrown 		cl = cl->cl_parent;
2758e005e804STrond Myklebust 
2759e005e804STrond Myklebust 	allow_signal(SIGKILL);
27604dc73c67SNeilBrown again:
2761e005e804STrond Myklebust 	nfs4_state_manager(clp);
2762956fd46fSTrond Myklebust 
2763956fd46fSTrond Myklebust 	if (test_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) &&
2764956fd46fSTrond Myklebust 	    !test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) {
27654dc73c67SNeilBrown 		wait_var_event_interruptible(&clp->cl_state,
27664dc73c67SNeilBrown 					     test_bit(NFS4CLNT_RUN_MANAGER,
27674dc73c67SNeilBrown 						      &clp->cl_state));
2768956fd46fSTrond Myklebust 		if (!atomic_read(&cl->cl_swapper))
2769956fd46fSTrond Myklebust 			clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
2770956fd46fSTrond Myklebust 		if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
2771956fd46fSTrond Myklebust 		    !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state))
27724dc73c67SNeilBrown 			goto again;
27734dc73c67SNeilBrown 		/* Either no longer a swapper, or were signalled */
27744dc73c67SNeilBrown 		clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
2775956fd46fSTrond Myklebust 	}
27764dc73c67SNeilBrown 
27774dc73c67SNeilBrown 	if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
27784dc73c67SNeilBrown 	    test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) &&
2779956fd46fSTrond Myklebust 	    !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state))
27804dc73c67SNeilBrown 		goto again;
27814dc73c67SNeilBrown 
2782e005e804STrond Myklebust 	nfs_put_client(clp);
2783080abad7SNeilBrown 	module_put_and_kthread_exit(0);
2784e005e804STrond Myklebust 	return 0;
27851da177e4SLinus Torvalds }
2786