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