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 ©->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(©->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 ©->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(©->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