18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * fs/nfs/nfs4state.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Client-side XDR for NFSv4. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2002 The Regents of the University of Michigan. 78c2ecf20Sopenharmony_ci * All rights reserved. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Kendrick Smith <kmsmith@umich.edu> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 128c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 138c2ecf20Sopenharmony_ci * are met: 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 168c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 178c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 188c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 198c2ecf20Sopenharmony_ci * documentation and/or other materials provided with the distribution. 208c2ecf20Sopenharmony_ci * 3. Neither the name of the University nor the names of its 218c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 228c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 258c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 268c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 278c2ecf20Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 288c2ecf20Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 298c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 308c2ecf20Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 318c2ecf20Sopenharmony_ci * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 328c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 338c2ecf20Sopenharmony_ci * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 348c2ecf20Sopenharmony_ci * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * Implementation of the NFSv4 state model. For the time being, 378c2ecf20Sopenharmony_ci * this is minimal, but will be made much more complex in a 388c2ecf20Sopenharmony_ci * subsequent patch. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include <linux/kernel.h> 428c2ecf20Sopenharmony_ci#include <linux/slab.h> 438c2ecf20Sopenharmony_ci#include <linux/fs.h> 448c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h> 458c2ecf20Sopenharmony_ci#include <linux/kthread.h> 468c2ecf20Sopenharmony_ci#include <linux/module.h> 478c2ecf20Sopenharmony_ci#include <linux/random.h> 488c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 498c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 508c2ecf20Sopenharmony_ci#include <linux/bitops.h> 518c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 528c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include <linux/sunrpc/clnt.h> 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#include "nfs4_fs.h" 578c2ecf20Sopenharmony_ci#include "callback.h" 588c2ecf20Sopenharmony_ci#include "delegation.h" 598c2ecf20Sopenharmony_ci#include "internal.h" 608c2ecf20Sopenharmony_ci#include "nfs4idmap.h" 618c2ecf20Sopenharmony_ci#include "nfs4session.h" 628c2ecf20Sopenharmony_ci#include "pnfs.h" 638c2ecf20Sopenharmony_ci#include "netns.h" 648c2ecf20Sopenharmony_ci#include "nfs4trace.h" 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_STATE 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define OPENOWNER_POOL_SIZE 8 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void nfs4_state_start_reclaim_reboot(struct nfs_client *clp); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciconst nfs4_stateid zero_stateid = { 738c2ecf20Sopenharmony_ci { .data = { 0 } }, 748c2ecf20Sopenharmony_ci .type = NFS4_SPECIAL_STATEID_TYPE, 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ciconst nfs4_stateid invalid_stateid = { 778c2ecf20Sopenharmony_ci { 788c2ecf20Sopenharmony_ci /* Funky initialiser keeps older gcc versions happy */ 798c2ecf20Sopenharmony_ci .data = { 0xff, 0xff, 0xff, 0xff, 0 }, 808c2ecf20Sopenharmony_ci }, 818c2ecf20Sopenharmony_ci .type = NFS4_INVALID_STATEID_TYPE, 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ciconst nfs4_stateid current_stateid = { 858c2ecf20Sopenharmony_ci { 868c2ecf20Sopenharmony_ci /* Funky initialiser keeps older gcc versions happy */ 878c2ecf20Sopenharmony_ci .data = { 0x0, 0x0, 0x0, 0x1, 0 }, 888c2ecf20Sopenharmony_ci }, 898c2ecf20Sopenharmony_ci .type = NFS4_SPECIAL_STATEID_TYPE, 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(nfs_clid_init_mutex); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int nfs4_setup_state_renewal(struct nfs_client *clp) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci int status; 978c2ecf20Sopenharmony_ci struct nfs_fsinfo fsinfo; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) { 1008c2ecf20Sopenharmony_ci nfs4_schedule_state_renewal(clp); 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci status = nfs4_proc_get_lease_time(clp, &fsinfo); 1058c2ecf20Sopenharmony_ci if (status == 0) { 1068c2ecf20Sopenharmony_ci nfs4_set_lease_period(clp, fsinfo.lease_time * HZ); 1078c2ecf20Sopenharmony_ci nfs4_schedule_state_renewal(clp); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return status; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ciint nfs4_init_clientid(struct nfs_client *clp, const struct cred *cred) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct nfs4_setclientid_res clid = { 1168c2ecf20Sopenharmony_ci .clientid = clp->cl_clientid, 1178c2ecf20Sopenharmony_ci .confirm = clp->cl_confirm, 1188c2ecf20Sopenharmony_ci }; 1198c2ecf20Sopenharmony_ci unsigned short port; 1208c2ecf20Sopenharmony_ci int status; 1218c2ecf20Sopenharmony_ci struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state)) 1248c2ecf20Sopenharmony_ci goto do_confirm; 1258c2ecf20Sopenharmony_ci port = nn->nfs_callback_tcpport; 1268c2ecf20Sopenharmony_ci if (clp->cl_addr.ss_family == AF_INET6) 1278c2ecf20Sopenharmony_ci port = nn->nfs_callback_tcpport6; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); 1308c2ecf20Sopenharmony_ci if (status != 0) 1318c2ecf20Sopenharmony_ci goto out; 1328c2ecf20Sopenharmony_ci clp->cl_clientid = clid.clientid; 1338c2ecf20Sopenharmony_ci clp->cl_confirm = clid.confirm; 1348c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); 1358c2ecf20Sopenharmony_cido_confirm: 1368c2ecf20Sopenharmony_ci status = nfs4_proc_setclientid_confirm(clp, &clid, cred); 1378c2ecf20Sopenharmony_ci if (status != 0) 1388c2ecf20Sopenharmony_ci goto out; 1398c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); 1408c2ecf20Sopenharmony_ci nfs4_setup_state_renewal(clp); 1418c2ecf20Sopenharmony_ciout: 1428c2ecf20Sopenharmony_ci return status; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/** 1468c2ecf20Sopenharmony_ci * nfs40_discover_server_trunking - Detect server IP address trunking (mv0) 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * @clp: nfs_client under test 1498c2ecf20Sopenharmony_ci * @result: OUT: found nfs_client, or clp 1508c2ecf20Sopenharmony_ci * @cred: credential to use for trunking test 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * Returns zero, a negative errno, or a negative NFS4ERR status. 1538c2ecf20Sopenharmony_ci * If zero is returned, an nfs_client pointer is planted in 1548c2ecf20Sopenharmony_ci * "result". 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * Note: The returned client may not yet be marked ready. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ciint nfs40_discover_server_trunking(struct nfs_client *clp, 1598c2ecf20Sopenharmony_ci struct nfs_client **result, 1608c2ecf20Sopenharmony_ci const struct cred *cred) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct nfs4_setclientid_res clid = { 1638c2ecf20Sopenharmony_ci .clientid = clp->cl_clientid, 1648c2ecf20Sopenharmony_ci .confirm = clp->cl_confirm, 1658c2ecf20Sopenharmony_ci }; 1668c2ecf20Sopenharmony_ci struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 1678c2ecf20Sopenharmony_ci unsigned short port; 1688c2ecf20Sopenharmony_ci int status; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci port = nn->nfs_callback_tcpport; 1718c2ecf20Sopenharmony_ci if (clp->cl_addr.ss_family == AF_INET6) 1728c2ecf20Sopenharmony_ci port = nn->nfs_callback_tcpport6; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); 1758c2ecf20Sopenharmony_ci if (status != 0) 1768c2ecf20Sopenharmony_ci goto out; 1778c2ecf20Sopenharmony_ci clp->cl_clientid = clid.clientid; 1788c2ecf20Sopenharmony_ci clp->cl_confirm = clid.confirm; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci status = nfs40_walk_client_list(clp, result, cred); 1818c2ecf20Sopenharmony_ci if (status == 0) { 1828c2ecf20Sopenharmony_ci /* Sustain the lease, even if it's empty. If the clientid4 1838c2ecf20Sopenharmony_ci * goes stale it's of no use for trunking discovery. */ 1848c2ecf20Sopenharmony_ci nfs4_schedule_state_renewal(*result); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* If the client state need to recover, do it. */ 1878c2ecf20Sopenharmony_ci if (clp->cl_state) 1888c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ciout: 1918c2ecf20Sopenharmony_ci return status; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ciconst struct cred *nfs4_get_machine_cred(struct nfs_client *clp) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci return get_cred(rpc_machine_cred()); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic void nfs4_root_machine_cred(struct nfs_client *clp) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Force root creds instead of machine */ 2038c2ecf20Sopenharmony_ci clp->cl_principal = NULL; 2048c2ecf20Sopenharmony_ci clp->cl_rpcclient->cl_principal = NULL; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic const struct cred * 2088c2ecf20Sopenharmony_cinfs4_get_renew_cred_server_locked(struct nfs_server *server) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci const struct cred *cred = NULL; 2118c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp; 2128c2ecf20Sopenharmony_ci struct rb_node *pos; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci for (pos = rb_first(&server->state_owners); 2158c2ecf20Sopenharmony_ci pos != NULL; 2168c2ecf20Sopenharmony_ci pos = rb_next(pos)) { 2178c2ecf20Sopenharmony_ci sp = rb_entry(pos, struct nfs4_state_owner, so_server_node); 2188c2ecf20Sopenharmony_ci if (list_empty(&sp->so_states)) 2198c2ecf20Sopenharmony_ci continue; 2208c2ecf20Sopenharmony_ci cred = get_cred(sp->so_cred); 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci return cred; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/** 2278c2ecf20Sopenharmony_ci * nfs4_get_renew_cred - Acquire credential for a renew operation 2288c2ecf20Sopenharmony_ci * @clp: client state handle 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * Returns an rpc_cred with reference count bumped, or NULL. 2318c2ecf20Sopenharmony_ci * Caller must hold clp->cl_lock. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ciconst struct cred *nfs4_get_renew_cred(struct nfs_client *clp) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci const struct cred *cred = NULL; 2368c2ecf20Sopenharmony_ci struct nfs_server *server; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Use machine credentials if available */ 2398c2ecf20Sopenharmony_ci cred = nfs4_get_machine_cred(clp); 2408c2ecf20Sopenharmony_ci if (cred != NULL) 2418c2ecf20Sopenharmony_ci goto out; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 2448c2ecf20Sopenharmony_ci rcu_read_lock(); 2458c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 2468c2ecf20Sopenharmony_ci cred = nfs4_get_renew_cred_server_locked(server); 2478c2ecf20Sopenharmony_ci if (cred != NULL) 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci rcu_read_unlock(); 2518c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ciout: 2548c2ecf20Sopenharmony_ci return cred; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void nfs4_end_drain_slot_table(struct nfs4_slot_table *tbl) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) { 2608c2ecf20Sopenharmony_ci spin_lock(&tbl->slot_tbl_lock); 2618c2ecf20Sopenharmony_ci nfs41_wake_slot_table(tbl); 2628c2ecf20Sopenharmony_ci spin_unlock(&tbl->slot_tbl_lock); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void nfs4_end_drain_session(struct nfs_client *clp) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct nfs4_session *ses = clp->cl_session; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (clp->cl_slot_tbl) { 2718c2ecf20Sopenharmony_ci nfs4_end_drain_slot_table(clp->cl_slot_tbl); 2728c2ecf20Sopenharmony_ci return; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (ses != NULL) { 2768c2ecf20Sopenharmony_ci nfs4_end_drain_slot_table(&ses->bc_slot_table); 2778c2ecf20Sopenharmony_ci nfs4_end_drain_slot_table(&ses->fc_slot_table); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int nfs4_drain_slot_tbl(struct nfs4_slot_table *tbl) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci set_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state); 2848c2ecf20Sopenharmony_ci spin_lock(&tbl->slot_tbl_lock); 2858c2ecf20Sopenharmony_ci if (tbl->highest_used_slotid != NFS4_NO_SLOT) { 2868c2ecf20Sopenharmony_ci reinit_completion(&tbl->complete); 2878c2ecf20Sopenharmony_ci spin_unlock(&tbl->slot_tbl_lock); 2888c2ecf20Sopenharmony_ci return wait_for_completion_interruptible(&tbl->complete); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci spin_unlock(&tbl->slot_tbl_lock); 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int nfs4_begin_drain_session(struct nfs_client *clp) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct nfs4_session *ses = clp->cl_session; 2978c2ecf20Sopenharmony_ci int ret; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (clp->cl_slot_tbl) 3008c2ecf20Sopenharmony_ci return nfs4_drain_slot_tbl(clp->cl_slot_tbl); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* back channel */ 3038c2ecf20Sopenharmony_ci ret = nfs4_drain_slot_tbl(&ses->bc_slot_table); 3048c2ecf20Sopenharmony_ci if (ret) 3058c2ecf20Sopenharmony_ci return ret; 3068c2ecf20Sopenharmony_ci /* fore channel */ 3078c2ecf20Sopenharmony_ci return nfs4_drain_slot_tbl(&ses->fc_slot_table); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci#if defined(CONFIG_NFS_V4_1) 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic void nfs41_finish_session_reset(struct nfs_client *clp) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); 3158c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); 3168c2ecf20Sopenharmony_ci /* create_session negotiated new slot table */ 3178c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state); 3188c2ecf20Sopenharmony_ci nfs4_setup_state_renewal(clp); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ciint nfs41_init_clientid(struct nfs_client *clp, const struct cred *cred) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci int status; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state)) 3268c2ecf20Sopenharmony_ci goto do_confirm; 3278c2ecf20Sopenharmony_ci status = nfs4_proc_exchange_id(clp, cred); 3288c2ecf20Sopenharmony_ci if (status != 0) 3298c2ecf20Sopenharmony_ci goto out; 3308c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); 3318c2ecf20Sopenharmony_cido_confirm: 3328c2ecf20Sopenharmony_ci status = nfs4_proc_create_session(clp, cred); 3338c2ecf20Sopenharmony_ci if (status != 0) 3348c2ecf20Sopenharmony_ci goto out; 3358c2ecf20Sopenharmony_ci if (!(clp->cl_exchange_flags & EXCHGID4_FLAG_CONFIRMED_R)) 3368c2ecf20Sopenharmony_ci nfs4_state_start_reclaim_reboot(clp); 3378c2ecf20Sopenharmony_ci nfs41_finish_session_reset(clp); 3388c2ecf20Sopenharmony_ci nfs_mark_client_ready(clp, NFS_CS_READY); 3398c2ecf20Sopenharmony_ciout: 3408c2ecf20Sopenharmony_ci return status; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/** 3448c2ecf20Sopenharmony_ci * nfs41_discover_server_trunking - Detect server IP address trunking (mv1) 3458c2ecf20Sopenharmony_ci * 3468c2ecf20Sopenharmony_ci * @clp: nfs_client under test 3478c2ecf20Sopenharmony_ci * @result: OUT: found nfs_client, or clp 3488c2ecf20Sopenharmony_ci * @cred: credential to use for trunking test 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci * Returns NFS4_OK, a negative errno, or a negative NFS4ERR status. 3518c2ecf20Sopenharmony_ci * If NFS4_OK is returned, an nfs_client pointer is planted in 3528c2ecf20Sopenharmony_ci * "result". 3538c2ecf20Sopenharmony_ci * 3548c2ecf20Sopenharmony_ci * Note: The returned client may not yet be marked ready. 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ciint nfs41_discover_server_trunking(struct nfs_client *clp, 3578c2ecf20Sopenharmony_ci struct nfs_client **result, 3588c2ecf20Sopenharmony_ci const struct cred *cred) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci int status; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci status = nfs4_proc_exchange_id(clp, cred); 3638c2ecf20Sopenharmony_ci if (status != NFS4_OK) 3648c2ecf20Sopenharmony_ci return status; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci status = nfs41_walk_client_list(clp, result, cred); 3678c2ecf20Sopenharmony_ci if (status < 0) 3688c2ecf20Sopenharmony_ci return status; 3698c2ecf20Sopenharmony_ci if (clp != *result) 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* 3738c2ecf20Sopenharmony_ci * Purge state if the client id was established in a prior 3748c2ecf20Sopenharmony_ci * instance and the client id could not have arrived on the 3758c2ecf20Sopenharmony_ci * server via Transparent State Migration. 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci if (clp->cl_exchange_flags & EXCHGID4_FLAG_CONFIRMED_R) { 3788c2ecf20Sopenharmony_ci if (!test_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags)) 3798c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state); 3808c2ecf20Sopenharmony_ci else 3818c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 3848c2ecf20Sopenharmony_ci status = nfs_wait_client_init_complete(clp); 3858c2ecf20Sopenharmony_ci if (status < 0) 3868c2ecf20Sopenharmony_ci nfs_put_client(clp); 3878c2ecf20Sopenharmony_ci return status; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */ 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/** 3938c2ecf20Sopenharmony_ci * nfs4_get_clid_cred - Acquire credential for a setclientid operation 3948c2ecf20Sopenharmony_ci * @clp: client state handle 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * Returns a cred with reference count bumped, or NULL. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ciconst struct cred *nfs4_get_clid_cred(struct nfs_client *clp) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci const struct cred *cred; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci cred = nfs4_get_machine_cred(clp); 4038c2ecf20Sopenharmony_ci return cred; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic struct nfs4_state_owner * 4078c2ecf20Sopenharmony_cinfs4_find_state_owner_locked(struct nfs_server *server, const struct cred *cred) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct rb_node **p = &server->state_owners.rb_node, 4108c2ecf20Sopenharmony_ci *parent = NULL; 4118c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp; 4128c2ecf20Sopenharmony_ci int cmp; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci while (*p != NULL) { 4158c2ecf20Sopenharmony_ci parent = *p; 4168c2ecf20Sopenharmony_ci sp = rb_entry(parent, struct nfs4_state_owner, so_server_node); 4178c2ecf20Sopenharmony_ci cmp = cred_fscmp(cred, sp->so_cred); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (cmp < 0) 4208c2ecf20Sopenharmony_ci p = &parent->rb_left; 4218c2ecf20Sopenharmony_ci else if (cmp > 0) 4228c2ecf20Sopenharmony_ci p = &parent->rb_right; 4238c2ecf20Sopenharmony_ci else { 4248c2ecf20Sopenharmony_ci if (!list_empty(&sp->so_lru)) 4258c2ecf20Sopenharmony_ci list_del_init(&sp->so_lru); 4268c2ecf20Sopenharmony_ci atomic_inc(&sp->so_count); 4278c2ecf20Sopenharmony_ci return sp; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci return NULL; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic struct nfs4_state_owner * 4348c2ecf20Sopenharmony_cinfs4_insert_state_owner_locked(struct nfs4_state_owner *new) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct nfs_server *server = new->so_server; 4378c2ecf20Sopenharmony_ci struct rb_node **p = &server->state_owners.rb_node, 4388c2ecf20Sopenharmony_ci *parent = NULL; 4398c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp; 4408c2ecf20Sopenharmony_ci int cmp; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci while (*p != NULL) { 4438c2ecf20Sopenharmony_ci parent = *p; 4448c2ecf20Sopenharmony_ci sp = rb_entry(parent, struct nfs4_state_owner, so_server_node); 4458c2ecf20Sopenharmony_ci cmp = cred_fscmp(new->so_cred, sp->so_cred); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (cmp < 0) 4488c2ecf20Sopenharmony_ci p = &parent->rb_left; 4498c2ecf20Sopenharmony_ci else if (cmp > 0) 4508c2ecf20Sopenharmony_ci p = &parent->rb_right; 4518c2ecf20Sopenharmony_ci else { 4528c2ecf20Sopenharmony_ci if (!list_empty(&sp->so_lru)) 4538c2ecf20Sopenharmony_ci list_del_init(&sp->so_lru); 4548c2ecf20Sopenharmony_ci atomic_inc(&sp->so_count); 4558c2ecf20Sopenharmony_ci return sp; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci rb_link_node(&new->so_server_node, parent, p); 4598c2ecf20Sopenharmony_ci rb_insert_color(&new->so_server_node, &server->state_owners); 4608c2ecf20Sopenharmony_ci return new; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic void 4648c2ecf20Sopenharmony_cinfs4_remove_state_owner_locked(struct nfs4_state_owner *sp) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct nfs_server *server = sp->so_server; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (!RB_EMPTY_NODE(&sp->so_server_node)) 4698c2ecf20Sopenharmony_ci rb_erase(&sp->so_server_node, &server->state_owners); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void 4738c2ecf20Sopenharmony_cinfs4_init_seqid_counter(struct nfs_seqid_counter *sc) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci sc->create_time = ktime_get(); 4768c2ecf20Sopenharmony_ci sc->flags = 0; 4778c2ecf20Sopenharmony_ci sc->counter = 0; 4788c2ecf20Sopenharmony_ci spin_lock_init(&sc->lock); 4798c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sc->list); 4808c2ecf20Sopenharmony_ci rpc_init_wait_queue(&sc->wait, "Seqid_waitqueue"); 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic void 4848c2ecf20Sopenharmony_cinfs4_destroy_seqid_counter(struct nfs_seqid_counter *sc) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci rpc_destroy_wait_queue(&sc->wait); 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci/* 4908c2ecf20Sopenharmony_ci * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to 4918c2ecf20Sopenharmony_ci * create a new state_owner. 4928c2ecf20Sopenharmony_ci * 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_cistatic struct nfs4_state_owner * 4958c2ecf20Sopenharmony_cinfs4_alloc_state_owner(struct nfs_server *server, 4968c2ecf20Sopenharmony_ci const struct cred *cred, 4978c2ecf20Sopenharmony_ci gfp_t gfp_flags) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci sp = kzalloc(sizeof(*sp), gfp_flags); 5028c2ecf20Sopenharmony_ci if (!sp) 5038c2ecf20Sopenharmony_ci return NULL; 5048c2ecf20Sopenharmony_ci sp->so_seqid.owner_id = ida_simple_get(&server->openowner_id, 0, 0, 5058c2ecf20Sopenharmony_ci gfp_flags); 5068c2ecf20Sopenharmony_ci if (sp->so_seqid.owner_id < 0) { 5078c2ecf20Sopenharmony_ci kfree(sp); 5088c2ecf20Sopenharmony_ci return NULL; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci sp->so_server = server; 5118c2ecf20Sopenharmony_ci sp->so_cred = get_cred(cred); 5128c2ecf20Sopenharmony_ci spin_lock_init(&sp->so_lock); 5138c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sp->so_states); 5148c2ecf20Sopenharmony_ci nfs4_init_seqid_counter(&sp->so_seqid); 5158c2ecf20Sopenharmony_ci atomic_set(&sp->so_count, 1); 5168c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sp->so_lru); 5178c2ecf20Sopenharmony_ci seqcount_spinlock_init(&sp->so_reclaim_seqcount, &sp->so_lock); 5188c2ecf20Sopenharmony_ci mutex_init(&sp->so_delegreturn_mutex); 5198c2ecf20Sopenharmony_ci return sp; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic void 5238c2ecf20Sopenharmony_cinfs4_reset_state_owner(struct nfs4_state_owner *sp) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci /* This state_owner is no longer usable, but must 5268c2ecf20Sopenharmony_ci * remain in place so that state recovery can find it 5278c2ecf20Sopenharmony_ci * and the opens associated with it. 5288c2ecf20Sopenharmony_ci * It may also be used for new 'open' request to 5298c2ecf20Sopenharmony_ci * return a delegation to the server. 5308c2ecf20Sopenharmony_ci * So update the 'create_time' so that it looks like 5318c2ecf20Sopenharmony_ci * a new state_owner. This will cause the server to 5328c2ecf20Sopenharmony_ci * request an OPEN_CONFIRM to start a new sequence. 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_ci sp->so_seqid.create_time = ktime_get(); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic void nfs4_free_state_owner(struct nfs4_state_owner *sp) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci nfs4_destroy_seqid_counter(&sp->so_seqid); 5408c2ecf20Sopenharmony_ci put_cred(sp->so_cred); 5418c2ecf20Sopenharmony_ci ida_simple_remove(&sp->so_server->openowner_id, sp->so_seqid.owner_id); 5428c2ecf20Sopenharmony_ci kfree(sp); 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic void nfs4_gc_state_owners(struct nfs_server *server) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 5488c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp, *tmp; 5498c2ecf20Sopenharmony_ci unsigned long time_min, time_max; 5508c2ecf20Sopenharmony_ci LIST_HEAD(doomed); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 5538c2ecf20Sopenharmony_ci time_max = jiffies; 5548c2ecf20Sopenharmony_ci time_min = (long)time_max - (long)clp->cl_lease_time; 5558c2ecf20Sopenharmony_ci list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) { 5568c2ecf20Sopenharmony_ci /* NB: LRU is sorted so that oldest is at the head */ 5578c2ecf20Sopenharmony_ci if (time_in_range(sp->so_expires, time_min, time_max)) 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci list_move(&sp->so_lru, &doomed); 5608c2ecf20Sopenharmony_ci nfs4_remove_state_owner_locked(sp); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci list_for_each_entry_safe(sp, tmp, &doomed, so_lru) { 5658c2ecf20Sopenharmony_ci list_del(&sp->so_lru); 5668c2ecf20Sopenharmony_ci nfs4_free_state_owner(sp); 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci/** 5718c2ecf20Sopenharmony_ci * nfs4_get_state_owner - Look up a state owner given a credential 5728c2ecf20Sopenharmony_ci * @server: nfs_server to search 5738c2ecf20Sopenharmony_ci * @cred: RPC credential to match 5748c2ecf20Sopenharmony_ci * @gfp_flags: allocation mode 5758c2ecf20Sopenharmony_ci * 5768c2ecf20Sopenharmony_ci * Returns a pointer to an instantiated nfs4_state_owner struct, or NULL. 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_cistruct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, 5798c2ecf20Sopenharmony_ci const struct cred *cred, 5808c2ecf20Sopenharmony_ci gfp_t gfp_flags) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 5838c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp, *new; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 5868c2ecf20Sopenharmony_ci sp = nfs4_find_state_owner_locked(server, cred); 5878c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 5888c2ecf20Sopenharmony_ci if (sp != NULL) 5898c2ecf20Sopenharmony_ci goto out; 5908c2ecf20Sopenharmony_ci new = nfs4_alloc_state_owner(server, cred, gfp_flags); 5918c2ecf20Sopenharmony_ci if (new == NULL) 5928c2ecf20Sopenharmony_ci goto out; 5938c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 5948c2ecf20Sopenharmony_ci sp = nfs4_insert_state_owner_locked(new); 5958c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 5968c2ecf20Sopenharmony_ci if (sp != new) 5978c2ecf20Sopenharmony_ci nfs4_free_state_owner(new); 5988c2ecf20Sopenharmony_ciout: 5998c2ecf20Sopenharmony_ci nfs4_gc_state_owners(server); 6008c2ecf20Sopenharmony_ci return sp; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/** 6048c2ecf20Sopenharmony_ci * nfs4_put_state_owner - Release a nfs4_state_owner 6058c2ecf20Sopenharmony_ci * @sp: state owner data to release 6068c2ecf20Sopenharmony_ci * 6078c2ecf20Sopenharmony_ci * Note that we keep released state owners on an LRU 6088c2ecf20Sopenharmony_ci * list. 6098c2ecf20Sopenharmony_ci * This caches valid state owners so that they can be 6108c2ecf20Sopenharmony_ci * reused, to avoid the OPEN_CONFIRM on minor version 0. 6118c2ecf20Sopenharmony_ci * It also pins the uniquifier of dropped state owners for 6128c2ecf20Sopenharmony_ci * a while, to ensure that those state owner names are 6138c2ecf20Sopenharmony_ci * never reused. 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_civoid nfs4_put_state_owner(struct nfs4_state_owner *sp) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct nfs_server *server = sp->so_server; 6188c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) 6218c2ecf20Sopenharmony_ci return; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci sp->so_expires = jiffies; 6248c2ecf20Sopenharmony_ci list_add_tail(&sp->so_lru, &server->state_owners_lru); 6258c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/** 6298c2ecf20Sopenharmony_ci * nfs4_purge_state_owners - Release all cached state owners 6308c2ecf20Sopenharmony_ci * @server: nfs_server with cached state owners to release 6318c2ecf20Sopenharmony_ci * @head: resulting list of state owners 6328c2ecf20Sopenharmony_ci * 6338c2ecf20Sopenharmony_ci * Called at umount time. Remaining state owners will be on 6348c2ecf20Sopenharmony_ci * the LRU with ref count of zero. 6358c2ecf20Sopenharmony_ci * Note that the state owners are not freed, but are added 6368c2ecf20Sopenharmony_ci * to the list @head, which can later be used as an argument 6378c2ecf20Sopenharmony_ci * to nfs4_free_state_owners. 6388c2ecf20Sopenharmony_ci */ 6398c2ecf20Sopenharmony_civoid nfs4_purge_state_owners(struct nfs_server *server, struct list_head *head) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 6428c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp, *tmp; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 6458c2ecf20Sopenharmony_ci list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) { 6468c2ecf20Sopenharmony_ci list_move(&sp->so_lru, head); 6478c2ecf20Sopenharmony_ci nfs4_remove_state_owner_locked(sp); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci/** 6538c2ecf20Sopenharmony_ci * nfs4_purge_state_owners - Release all cached state owners 6548c2ecf20Sopenharmony_ci * @head: resulting list of state owners 6558c2ecf20Sopenharmony_ci * 6568c2ecf20Sopenharmony_ci * Frees a list of state owners that was generated by 6578c2ecf20Sopenharmony_ci * nfs4_purge_state_owners 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_civoid nfs4_free_state_owners(struct list_head *head) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp, *tmp; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci list_for_each_entry_safe(sp, tmp, head, so_lru) { 6648c2ecf20Sopenharmony_ci list_del(&sp->so_lru); 6658c2ecf20Sopenharmony_ci nfs4_free_state_owner(sp); 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic struct nfs4_state * 6708c2ecf20Sopenharmony_cinfs4_alloc_open_state(void) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct nfs4_state *state; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_NOFS); 6758c2ecf20Sopenharmony_ci if (!state) 6768c2ecf20Sopenharmony_ci return NULL; 6778c2ecf20Sopenharmony_ci refcount_set(&state->count, 1); 6788c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&state->lock_states); 6798c2ecf20Sopenharmony_ci spin_lock_init(&state->state_lock); 6808c2ecf20Sopenharmony_ci seqlock_init(&state->seqlock); 6818c2ecf20Sopenharmony_ci init_waitqueue_head(&state->waitq); 6828c2ecf20Sopenharmony_ci return state; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_civoid 6868c2ecf20Sopenharmony_cinfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci if (state->state == fmode) 6898c2ecf20Sopenharmony_ci return; 6908c2ecf20Sopenharmony_ci /* NB! List reordering - see the reclaim code for why. */ 6918c2ecf20Sopenharmony_ci if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) { 6928c2ecf20Sopenharmony_ci if (fmode & FMODE_WRITE) 6938c2ecf20Sopenharmony_ci list_move(&state->open_states, &state->owner->so_states); 6948c2ecf20Sopenharmony_ci else 6958c2ecf20Sopenharmony_ci list_move_tail(&state->open_states, &state->owner->so_states); 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci state->state = fmode; 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic struct nfs4_state * 7018c2ecf20Sopenharmony_ci__nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 7048c2ecf20Sopenharmony_ci struct nfs4_state *state; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci list_for_each_entry_rcu(state, &nfsi->open_states, inode_states) { 7078c2ecf20Sopenharmony_ci if (state->owner != owner) 7088c2ecf20Sopenharmony_ci continue; 7098c2ecf20Sopenharmony_ci if (!nfs4_valid_open_stateid(state)) 7108c2ecf20Sopenharmony_ci continue; 7118c2ecf20Sopenharmony_ci if (refcount_inc_not_zero(&state->count)) 7128c2ecf20Sopenharmony_ci return state; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci return NULL; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic void 7188c2ecf20Sopenharmony_cinfs4_free_open_state(struct nfs4_state *state) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci kfree_rcu(state, rcu_head); 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistruct nfs4_state * 7248c2ecf20Sopenharmony_cinfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct nfs4_state *state, *new; 7278c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci rcu_read_lock(); 7308c2ecf20Sopenharmony_ci state = __nfs4_find_state_byowner(inode, owner); 7318c2ecf20Sopenharmony_ci rcu_read_unlock(); 7328c2ecf20Sopenharmony_ci if (state) 7338c2ecf20Sopenharmony_ci goto out; 7348c2ecf20Sopenharmony_ci new = nfs4_alloc_open_state(); 7358c2ecf20Sopenharmony_ci spin_lock(&owner->so_lock); 7368c2ecf20Sopenharmony_ci spin_lock(&inode->i_lock); 7378c2ecf20Sopenharmony_ci state = __nfs4_find_state_byowner(inode, owner); 7388c2ecf20Sopenharmony_ci if (state == NULL && new != NULL) { 7398c2ecf20Sopenharmony_ci state = new; 7408c2ecf20Sopenharmony_ci state->owner = owner; 7418c2ecf20Sopenharmony_ci atomic_inc(&owner->so_count); 7428c2ecf20Sopenharmony_ci ihold(inode); 7438c2ecf20Sopenharmony_ci state->inode = inode; 7448c2ecf20Sopenharmony_ci list_add_rcu(&state->inode_states, &nfsi->open_states); 7458c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 7468c2ecf20Sopenharmony_ci /* Note: The reclaim code dictates that we add stateless 7478c2ecf20Sopenharmony_ci * and read-only stateids to the end of the list */ 7488c2ecf20Sopenharmony_ci list_add_tail(&state->open_states, &owner->so_states); 7498c2ecf20Sopenharmony_ci spin_unlock(&owner->so_lock); 7508c2ecf20Sopenharmony_ci } else { 7518c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 7528c2ecf20Sopenharmony_ci spin_unlock(&owner->so_lock); 7538c2ecf20Sopenharmony_ci if (new) 7548c2ecf20Sopenharmony_ci nfs4_free_open_state(new); 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ciout: 7578c2ecf20Sopenharmony_ci return state; 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_civoid nfs4_put_open_state(struct nfs4_state *state) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci struct inode *inode = state->inode; 7638c2ecf20Sopenharmony_ci struct nfs4_state_owner *owner = state->owner; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci if (!refcount_dec_and_lock(&state->count, &owner->so_lock)) 7668c2ecf20Sopenharmony_ci return; 7678c2ecf20Sopenharmony_ci spin_lock(&inode->i_lock); 7688c2ecf20Sopenharmony_ci list_del_rcu(&state->inode_states); 7698c2ecf20Sopenharmony_ci list_del(&state->open_states); 7708c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 7718c2ecf20Sopenharmony_ci spin_unlock(&owner->so_lock); 7728c2ecf20Sopenharmony_ci nfs4_inode_return_delegation_on_close(inode); 7738c2ecf20Sopenharmony_ci iput(inode); 7748c2ecf20Sopenharmony_ci nfs4_free_open_state(state); 7758c2ecf20Sopenharmony_ci nfs4_put_state_owner(owner); 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci/* 7798c2ecf20Sopenharmony_ci * Close the current file. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_cistatic void __nfs4_close(struct nfs4_state *state, 7828c2ecf20Sopenharmony_ci fmode_t fmode, gfp_t gfp_mask, int wait) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct nfs4_state_owner *owner = state->owner; 7858c2ecf20Sopenharmony_ci int call_close = 0; 7868c2ecf20Sopenharmony_ci fmode_t newstate; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci atomic_inc(&owner->so_count); 7898c2ecf20Sopenharmony_ci /* Protect against nfs4_find_state() */ 7908c2ecf20Sopenharmony_ci spin_lock(&owner->so_lock); 7918c2ecf20Sopenharmony_ci switch (fmode & (FMODE_READ | FMODE_WRITE)) { 7928c2ecf20Sopenharmony_ci case FMODE_READ: 7938c2ecf20Sopenharmony_ci state->n_rdonly--; 7948c2ecf20Sopenharmony_ci break; 7958c2ecf20Sopenharmony_ci case FMODE_WRITE: 7968c2ecf20Sopenharmony_ci state->n_wronly--; 7978c2ecf20Sopenharmony_ci break; 7988c2ecf20Sopenharmony_ci case FMODE_READ|FMODE_WRITE: 7998c2ecf20Sopenharmony_ci state->n_rdwr--; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci newstate = FMODE_READ|FMODE_WRITE; 8028c2ecf20Sopenharmony_ci if (state->n_rdwr == 0) { 8038c2ecf20Sopenharmony_ci if (state->n_rdonly == 0) { 8048c2ecf20Sopenharmony_ci newstate &= ~FMODE_READ; 8058c2ecf20Sopenharmony_ci call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags); 8068c2ecf20Sopenharmony_ci call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci if (state->n_wronly == 0) { 8098c2ecf20Sopenharmony_ci newstate &= ~FMODE_WRITE; 8108c2ecf20Sopenharmony_ci call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags); 8118c2ecf20Sopenharmony_ci call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci if (newstate == 0) 8148c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATED_STATE, &state->flags); 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci nfs4_state_set_mode_locked(state, newstate); 8178c2ecf20Sopenharmony_ci spin_unlock(&owner->so_lock); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (!call_close) { 8208c2ecf20Sopenharmony_ci nfs4_put_open_state(state); 8218c2ecf20Sopenharmony_ci nfs4_put_state_owner(owner); 8228c2ecf20Sopenharmony_ci } else 8238c2ecf20Sopenharmony_ci nfs4_do_close(state, gfp_mask, wait); 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_civoid nfs4_close_state(struct nfs4_state *state, fmode_t fmode) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci __nfs4_close(state, fmode, GFP_NOFS, 0); 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_civoid nfs4_close_sync(struct nfs4_state *state, fmode_t fmode) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci __nfs4_close(state, fmode, GFP_KERNEL, 1); 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci/* 8378c2ecf20Sopenharmony_ci * Search the state->lock_states for an existing lock_owner 8388c2ecf20Sopenharmony_ci * that is compatible with either of the given owners. 8398c2ecf20Sopenharmony_ci * If the second is non-zero, then the first refers to a Posix-lock 8408c2ecf20Sopenharmony_ci * owner (current->files) and the second refers to a flock/OFD 8418c2ecf20Sopenharmony_ci * owner (struct file*). In that case, prefer a match for the first 8428c2ecf20Sopenharmony_ci * owner. 8438c2ecf20Sopenharmony_ci * If both sorts of locks are held on the one file we cannot know 8448c2ecf20Sopenharmony_ci * which stateid was intended to be used, so a "correct" choice cannot 8458c2ecf20Sopenharmony_ci * be made. Failing that, a "consistent" choice is preferable. The 8468c2ecf20Sopenharmony_ci * consistent choice we make is to prefer the first owner, that of a 8478c2ecf20Sopenharmony_ci * Posix lock. 8488c2ecf20Sopenharmony_ci */ 8498c2ecf20Sopenharmony_cistatic struct nfs4_lock_state * 8508c2ecf20Sopenharmony_ci__nfs4_find_lock_state(struct nfs4_state *state, 8518c2ecf20Sopenharmony_ci fl_owner_t fl_owner, fl_owner_t fl_owner2) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct nfs4_lock_state *pos, *ret = NULL; 8548c2ecf20Sopenharmony_ci list_for_each_entry(pos, &state->lock_states, ls_locks) { 8558c2ecf20Sopenharmony_ci if (pos->ls_owner == fl_owner) { 8568c2ecf20Sopenharmony_ci ret = pos; 8578c2ecf20Sopenharmony_ci break; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci if (pos->ls_owner == fl_owner2) 8608c2ecf20Sopenharmony_ci ret = pos; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci if (ret) 8638c2ecf20Sopenharmony_ci refcount_inc(&ret->ls_count); 8648c2ecf20Sopenharmony_ci return ret; 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/* 8688c2ecf20Sopenharmony_ci * Return a compatible lock_state. If no initialized lock_state structure 8698c2ecf20Sopenharmony_ci * exists, return an uninitialized one. 8708c2ecf20Sopenharmony_ci * 8718c2ecf20Sopenharmony_ci */ 8728c2ecf20Sopenharmony_cistatic struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci struct nfs4_lock_state *lsp; 8758c2ecf20Sopenharmony_ci struct nfs_server *server = state->owner->so_server; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci lsp = kzalloc(sizeof(*lsp), GFP_NOFS); 8788c2ecf20Sopenharmony_ci if (lsp == NULL) 8798c2ecf20Sopenharmony_ci return NULL; 8808c2ecf20Sopenharmony_ci nfs4_init_seqid_counter(&lsp->ls_seqid); 8818c2ecf20Sopenharmony_ci refcount_set(&lsp->ls_count, 1); 8828c2ecf20Sopenharmony_ci lsp->ls_state = state; 8838c2ecf20Sopenharmony_ci lsp->ls_owner = fl_owner; 8848c2ecf20Sopenharmony_ci lsp->ls_seqid.owner_id = ida_simple_get(&server->lockowner_id, 0, 0, GFP_NOFS); 8858c2ecf20Sopenharmony_ci if (lsp->ls_seqid.owner_id < 0) 8868c2ecf20Sopenharmony_ci goto out_free; 8878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lsp->ls_locks); 8888c2ecf20Sopenharmony_ci return lsp; 8898c2ecf20Sopenharmony_ciout_free: 8908c2ecf20Sopenharmony_ci kfree(lsp); 8918c2ecf20Sopenharmony_ci return NULL; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_civoid nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci ida_simple_remove(&server->lockowner_id, lsp->ls_seqid.owner_id); 8978c2ecf20Sopenharmony_ci nfs4_destroy_seqid_counter(&lsp->ls_seqid); 8988c2ecf20Sopenharmony_ci kfree(lsp); 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci/* 9028c2ecf20Sopenharmony_ci * Return a compatible lock_state. If no initialized lock_state structure 9038c2ecf20Sopenharmony_ci * exists, return an uninitialized one. 9048c2ecf20Sopenharmony_ci * 9058c2ecf20Sopenharmony_ci */ 9068c2ecf20Sopenharmony_cistatic struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci struct nfs4_lock_state *lsp, *new = NULL; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci for(;;) { 9118c2ecf20Sopenharmony_ci spin_lock(&state->state_lock); 9128c2ecf20Sopenharmony_ci lsp = __nfs4_find_lock_state(state, owner, NULL); 9138c2ecf20Sopenharmony_ci if (lsp != NULL) 9148c2ecf20Sopenharmony_ci break; 9158c2ecf20Sopenharmony_ci if (new != NULL) { 9168c2ecf20Sopenharmony_ci list_add(&new->ls_locks, &state->lock_states); 9178c2ecf20Sopenharmony_ci set_bit(LK_STATE_IN_USE, &state->flags); 9188c2ecf20Sopenharmony_ci lsp = new; 9198c2ecf20Sopenharmony_ci new = NULL; 9208c2ecf20Sopenharmony_ci break; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci spin_unlock(&state->state_lock); 9238c2ecf20Sopenharmony_ci new = nfs4_alloc_lock_state(state, owner); 9248c2ecf20Sopenharmony_ci if (new == NULL) 9258c2ecf20Sopenharmony_ci return NULL; 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci spin_unlock(&state->state_lock); 9288c2ecf20Sopenharmony_ci if (new != NULL) 9298c2ecf20Sopenharmony_ci nfs4_free_lock_state(state->owner->so_server, new); 9308c2ecf20Sopenharmony_ci return lsp; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci/* 9348c2ecf20Sopenharmony_ci * Release reference to lock_state, and free it if we see that 9358c2ecf20Sopenharmony_ci * it is no longer in use 9368c2ecf20Sopenharmony_ci */ 9378c2ecf20Sopenharmony_civoid nfs4_put_lock_state(struct nfs4_lock_state *lsp) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci struct nfs_server *server; 9408c2ecf20Sopenharmony_ci struct nfs4_state *state; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (lsp == NULL) 9438c2ecf20Sopenharmony_ci return; 9448c2ecf20Sopenharmony_ci state = lsp->ls_state; 9458c2ecf20Sopenharmony_ci if (!refcount_dec_and_lock(&lsp->ls_count, &state->state_lock)) 9468c2ecf20Sopenharmony_ci return; 9478c2ecf20Sopenharmony_ci list_del(&lsp->ls_locks); 9488c2ecf20Sopenharmony_ci if (list_empty(&state->lock_states)) 9498c2ecf20Sopenharmony_ci clear_bit(LK_STATE_IN_USE, &state->flags); 9508c2ecf20Sopenharmony_ci spin_unlock(&state->state_lock); 9518c2ecf20Sopenharmony_ci server = state->owner->so_server; 9528c2ecf20Sopenharmony_ci if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) { 9538c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci clp->cl_mvops->free_lock_state(server, lsp); 9568c2ecf20Sopenharmony_ci } else 9578c2ecf20Sopenharmony_ci nfs4_free_lock_state(server, lsp); 9588c2ecf20Sopenharmony_ci} 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_cistatic void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci struct nfs4_lock_state *lsp = src->fl_u.nfs4_fl.owner; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci dst->fl_u.nfs4_fl.owner = lsp; 9658c2ecf20Sopenharmony_ci refcount_inc(&lsp->ls_count); 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic void nfs4_fl_release_lock(struct file_lock *fl) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci nfs4_put_lock_state(fl->fl_u.nfs4_fl.owner); 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cistatic const struct file_lock_operations nfs4_fl_lock_ops = { 9748c2ecf20Sopenharmony_ci .fl_copy_lock = nfs4_fl_copy_lock, 9758c2ecf20Sopenharmony_ci .fl_release_private = nfs4_fl_release_lock, 9768c2ecf20Sopenharmony_ci}; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ciint nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct nfs4_lock_state *lsp; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (fl->fl_ops != NULL) 9838c2ecf20Sopenharmony_ci return 0; 9848c2ecf20Sopenharmony_ci lsp = nfs4_get_lock_state(state, fl->fl_owner); 9858c2ecf20Sopenharmony_ci if (lsp == NULL) 9868c2ecf20Sopenharmony_ci return -ENOMEM; 9878c2ecf20Sopenharmony_ci fl->fl_u.nfs4_fl.owner = lsp; 9888c2ecf20Sopenharmony_ci fl->fl_ops = &nfs4_fl_lock_ops; 9898c2ecf20Sopenharmony_ci return 0; 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic int nfs4_copy_lock_stateid(nfs4_stateid *dst, 9938c2ecf20Sopenharmony_ci struct nfs4_state *state, 9948c2ecf20Sopenharmony_ci const struct nfs_lock_context *l_ctx) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct nfs4_lock_state *lsp; 9978c2ecf20Sopenharmony_ci fl_owner_t fl_owner, fl_flock_owner; 9988c2ecf20Sopenharmony_ci int ret = -ENOENT; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci if (l_ctx == NULL) 10018c2ecf20Sopenharmony_ci goto out; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) 10048c2ecf20Sopenharmony_ci goto out; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci fl_owner = l_ctx->lockowner; 10078c2ecf20Sopenharmony_ci fl_flock_owner = l_ctx->open_context->flock_owner; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci spin_lock(&state->state_lock); 10108c2ecf20Sopenharmony_ci lsp = __nfs4_find_lock_state(state, fl_owner, fl_flock_owner); 10118c2ecf20Sopenharmony_ci if (lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags)) 10128c2ecf20Sopenharmony_ci ret = -EIO; 10138c2ecf20Sopenharmony_ci else if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) { 10148c2ecf20Sopenharmony_ci nfs4_stateid_copy(dst, &lsp->ls_stateid); 10158c2ecf20Sopenharmony_ci ret = 0; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci spin_unlock(&state->state_lock); 10188c2ecf20Sopenharmony_ci nfs4_put_lock_state(lsp); 10198c2ecf20Sopenharmony_ciout: 10208c2ecf20Sopenharmony_ci return ret; 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cibool nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state) 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci bool ret; 10268c2ecf20Sopenharmony_ci const nfs4_stateid *src; 10278c2ecf20Sopenharmony_ci int seq; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci do { 10308c2ecf20Sopenharmony_ci ret = false; 10318c2ecf20Sopenharmony_ci src = &zero_stateid; 10328c2ecf20Sopenharmony_ci seq = read_seqbegin(&state->seqlock); 10338c2ecf20Sopenharmony_ci if (test_bit(NFS_OPEN_STATE, &state->flags)) { 10348c2ecf20Sopenharmony_ci src = &state->open_stateid; 10358c2ecf20Sopenharmony_ci ret = true; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci nfs4_stateid_copy(dst, src); 10388c2ecf20Sopenharmony_ci } while (read_seqretry(&state->seqlock, seq)); 10398c2ecf20Sopenharmony_ci return ret; 10408c2ecf20Sopenharmony_ci} 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci/* 10438c2ecf20Sopenharmony_ci * Byte-range lock aware utility to initialize the stateid of read/write 10448c2ecf20Sopenharmony_ci * requests. 10458c2ecf20Sopenharmony_ci */ 10468c2ecf20Sopenharmony_ciint nfs4_select_rw_stateid(struct nfs4_state *state, 10478c2ecf20Sopenharmony_ci fmode_t fmode, const struct nfs_lock_context *l_ctx, 10488c2ecf20Sopenharmony_ci nfs4_stateid *dst, const struct cred **cred) 10498c2ecf20Sopenharmony_ci{ 10508c2ecf20Sopenharmony_ci int ret; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (!nfs4_valid_open_stateid(state)) 10538c2ecf20Sopenharmony_ci return -EIO; 10548c2ecf20Sopenharmony_ci if (cred != NULL) 10558c2ecf20Sopenharmony_ci *cred = NULL; 10568c2ecf20Sopenharmony_ci ret = nfs4_copy_lock_stateid(dst, state, l_ctx); 10578c2ecf20Sopenharmony_ci if (ret == -EIO) 10588c2ecf20Sopenharmony_ci /* A lost lock - don't even consider delegations */ 10598c2ecf20Sopenharmony_ci goto out; 10608c2ecf20Sopenharmony_ci /* returns true if delegation stateid found and copied */ 10618c2ecf20Sopenharmony_ci if (nfs4_copy_delegation_stateid(state->inode, fmode, dst, cred)) { 10628c2ecf20Sopenharmony_ci ret = 0; 10638c2ecf20Sopenharmony_ci goto out; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci if (ret != -ENOENT) 10668c2ecf20Sopenharmony_ci /* nfs4_copy_delegation_stateid() didn't over-write 10678c2ecf20Sopenharmony_ci * dst, so it still has the lock stateid which we now 10688c2ecf20Sopenharmony_ci * choose to use. 10698c2ecf20Sopenharmony_ci */ 10708c2ecf20Sopenharmony_ci goto out; 10718c2ecf20Sopenharmony_ci ret = nfs4_copy_open_stateid(dst, state) ? 0 : -EAGAIN; 10728c2ecf20Sopenharmony_ciout: 10738c2ecf20Sopenharmony_ci if (nfs_server_capable(state->inode, NFS_CAP_STATEID_NFSV41)) 10748c2ecf20Sopenharmony_ci dst->seqid = 0; 10758c2ecf20Sopenharmony_ci return ret; 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_cistruct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci struct nfs_seqid *new; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci new = kmalloc(sizeof(*new), gfp_mask); 10838c2ecf20Sopenharmony_ci if (new == NULL) 10848c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 10858c2ecf20Sopenharmony_ci new->sequence = counter; 10868c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&new->list); 10878c2ecf20Sopenharmony_ci new->task = NULL; 10888c2ecf20Sopenharmony_ci return new; 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_civoid nfs_release_seqid(struct nfs_seqid *seqid) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci struct nfs_seqid_counter *sequence; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (seqid == NULL || list_empty(&seqid->list)) 10968c2ecf20Sopenharmony_ci return; 10978c2ecf20Sopenharmony_ci sequence = seqid->sequence; 10988c2ecf20Sopenharmony_ci spin_lock(&sequence->lock); 10998c2ecf20Sopenharmony_ci list_del_init(&seqid->list); 11008c2ecf20Sopenharmony_ci if (!list_empty(&sequence->list)) { 11018c2ecf20Sopenharmony_ci struct nfs_seqid *next; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci next = list_first_entry(&sequence->list, 11048c2ecf20Sopenharmony_ci struct nfs_seqid, list); 11058c2ecf20Sopenharmony_ci rpc_wake_up_queued_task(&sequence->wait, next->task); 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci spin_unlock(&sequence->lock); 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_civoid nfs_free_seqid(struct nfs_seqid *seqid) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci nfs_release_seqid(seqid); 11138c2ecf20Sopenharmony_ci kfree(seqid); 11148c2ecf20Sopenharmony_ci} 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci/* 11178c2ecf20Sopenharmony_ci * Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or 11188c2ecf20Sopenharmony_ci * failed with a seqid incrementing error - 11198c2ecf20Sopenharmony_ci * see comments nfs4.h:seqid_mutating_error() 11208c2ecf20Sopenharmony_ci */ 11218c2ecf20Sopenharmony_cistatic void nfs_increment_seqid(int status, struct nfs_seqid *seqid) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci switch (status) { 11248c2ecf20Sopenharmony_ci case 0: 11258c2ecf20Sopenharmony_ci break; 11268c2ecf20Sopenharmony_ci case -NFS4ERR_BAD_SEQID: 11278c2ecf20Sopenharmony_ci if (seqid->sequence->flags & NFS_SEQID_CONFIRMED) 11288c2ecf20Sopenharmony_ci return; 11298c2ecf20Sopenharmony_ci pr_warn_ratelimited("NFS: v4 server returned a bad" 11308c2ecf20Sopenharmony_ci " sequence-id error on an" 11318c2ecf20Sopenharmony_ci " unconfirmed sequence %p!\n", 11328c2ecf20Sopenharmony_ci seqid->sequence); 11338c2ecf20Sopenharmony_ci case -NFS4ERR_STALE_CLIENTID: 11348c2ecf20Sopenharmony_ci case -NFS4ERR_STALE_STATEID: 11358c2ecf20Sopenharmony_ci case -NFS4ERR_BAD_STATEID: 11368c2ecf20Sopenharmony_ci case -NFS4ERR_BADXDR: 11378c2ecf20Sopenharmony_ci case -NFS4ERR_RESOURCE: 11388c2ecf20Sopenharmony_ci case -NFS4ERR_NOFILEHANDLE: 11398c2ecf20Sopenharmony_ci case -NFS4ERR_MOVED: 11408c2ecf20Sopenharmony_ci /* Non-seqid mutating errors */ 11418c2ecf20Sopenharmony_ci return; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci /* 11448c2ecf20Sopenharmony_ci * Note: no locking needed as we are guaranteed to be first 11458c2ecf20Sopenharmony_ci * on the sequence list 11468c2ecf20Sopenharmony_ci */ 11478c2ecf20Sopenharmony_ci seqid->sequence->counter++; 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_civoid nfs_increment_open_seqid(int status, struct nfs_seqid *seqid) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci if (seqid == NULL) 11558c2ecf20Sopenharmony_ci return; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci sp = container_of(seqid->sequence, struct nfs4_state_owner, so_seqid); 11588c2ecf20Sopenharmony_ci if (status == -NFS4ERR_BAD_SEQID) 11598c2ecf20Sopenharmony_ci nfs4_reset_state_owner(sp); 11608c2ecf20Sopenharmony_ci if (!nfs4_has_session(sp->so_server->nfs_client)) 11618c2ecf20Sopenharmony_ci nfs_increment_seqid(status, seqid); 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci/* 11658c2ecf20Sopenharmony_ci * Increment the seqid if the LOCK/LOCKU succeeded, or 11668c2ecf20Sopenharmony_ci * failed with a seqid incrementing error - 11678c2ecf20Sopenharmony_ci * see comments nfs4.h:seqid_mutating_error() 11688c2ecf20Sopenharmony_ci */ 11698c2ecf20Sopenharmony_civoid nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid) 11708c2ecf20Sopenharmony_ci{ 11718c2ecf20Sopenharmony_ci if (seqid != NULL) 11728c2ecf20Sopenharmony_ci nfs_increment_seqid(status, seqid); 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ciint nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task) 11768c2ecf20Sopenharmony_ci{ 11778c2ecf20Sopenharmony_ci struct nfs_seqid_counter *sequence; 11788c2ecf20Sopenharmony_ci int status = 0; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci if (seqid == NULL) 11818c2ecf20Sopenharmony_ci goto out; 11828c2ecf20Sopenharmony_ci sequence = seqid->sequence; 11838c2ecf20Sopenharmony_ci spin_lock(&sequence->lock); 11848c2ecf20Sopenharmony_ci seqid->task = task; 11858c2ecf20Sopenharmony_ci if (list_empty(&seqid->list)) 11868c2ecf20Sopenharmony_ci list_add_tail(&seqid->list, &sequence->list); 11878c2ecf20Sopenharmony_ci if (list_first_entry(&sequence->list, struct nfs_seqid, list) == seqid) 11888c2ecf20Sopenharmony_ci goto unlock; 11898c2ecf20Sopenharmony_ci rpc_sleep_on(&sequence->wait, task, NULL); 11908c2ecf20Sopenharmony_ci status = -EAGAIN; 11918c2ecf20Sopenharmony_ciunlock: 11928c2ecf20Sopenharmony_ci spin_unlock(&sequence->lock); 11938c2ecf20Sopenharmony_ciout: 11948c2ecf20Sopenharmony_ci return status; 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_cistatic int nfs4_run_state_manager(void *); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic void nfs4_clear_state_manager_bit(struct nfs_client *clp) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci smp_mb__before_atomic(); 12028c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state); 12038c2ecf20Sopenharmony_ci smp_mb__after_atomic(); 12048c2ecf20Sopenharmony_ci wake_up_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING); 12058c2ecf20Sopenharmony_ci rpc_wake_up(&clp->cl_rpcwaitq); 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci/* 12098c2ecf20Sopenharmony_ci * Schedule the nfs_client asynchronous state management routine 12108c2ecf20Sopenharmony_ci */ 12118c2ecf20Sopenharmony_civoid nfs4_schedule_state_manager(struct nfs_client *clp) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct task_struct *task; 12148c2ecf20Sopenharmony_ci char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1]; 12158c2ecf20Sopenharmony_ci struct rpc_clnt *clnt = clp->cl_rpcclient; 12168c2ecf20Sopenharmony_ci bool swapon = false; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (atomic_read(&clnt->cl_swapper)) { 12218c2ecf20Sopenharmony_ci swapon = !test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, 12228c2ecf20Sopenharmony_ci &clp->cl_state); 12238c2ecf20Sopenharmony_ci if (!swapon) { 12248c2ecf20Sopenharmony_ci wake_up_var(&clp->cl_state); 12258c2ecf20Sopenharmony_ci return; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) 12308c2ecf20Sopenharmony_ci return; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci __module_get(THIS_MODULE); 12338c2ecf20Sopenharmony_ci refcount_inc(&clp->cl_count); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* The rcu_read_lock() is not strictly necessary, as the state 12368c2ecf20Sopenharmony_ci * manager is the only thread that ever changes the rpc_xprt 12378c2ecf20Sopenharmony_ci * after it's initialized. At this point, we're single threaded. */ 12388c2ecf20Sopenharmony_ci rcu_read_lock(); 12398c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), "%s-manager", 12408c2ecf20Sopenharmony_ci rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 12418c2ecf20Sopenharmony_ci rcu_read_unlock(); 12428c2ecf20Sopenharmony_ci task = kthread_run(nfs4_run_state_manager, clp, "%s", buf); 12438c2ecf20Sopenharmony_ci if (IS_ERR(task)) { 12448c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: kthread_run: %ld\n", 12458c2ecf20Sopenharmony_ci __func__, PTR_ERR(task)); 12468c2ecf20Sopenharmony_ci if (!nfs_client_init_is_complete(clp)) 12478c2ecf20Sopenharmony_ci nfs_mark_client_ready(clp, PTR_ERR(task)); 12488c2ecf20Sopenharmony_ci if (swapon) 12498c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state); 12508c2ecf20Sopenharmony_ci nfs4_clear_state_manager_bit(clp); 12518c2ecf20Sopenharmony_ci nfs_put_client(clp); 12528c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci/* 12578c2ecf20Sopenharmony_ci * Schedule a lease recovery attempt 12588c2ecf20Sopenharmony_ci */ 12598c2ecf20Sopenharmony_civoid nfs4_schedule_lease_recovery(struct nfs_client *clp) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci if (!clp) 12628c2ecf20Sopenharmony_ci return; 12638c2ecf20Sopenharmony_ci if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) 12648c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); 12658c2ecf20Sopenharmony_ci dprintk("%s: scheduling lease recovery for server %s\n", __func__, 12668c2ecf20Sopenharmony_ci clp->cl_hostname); 12678c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_schedule_lease_recovery); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci/** 12728c2ecf20Sopenharmony_ci * nfs4_schedule_migration_recovery - trigger migration recovery 12738c2ecf20Sopenharmony_ci * 12748c2ecf20Sopenharmony_ci * @server: FSID that is migrating 12758c2ecf20Sopenharmony_ci * 12768c2ecf20Sopenharmony_ci * Returns zero if recovery has started, otherwise a negative NFS4ERR 12778c2ecf20Sopenharmony_ci * value is returned. 12788c2ecf20Sopenharmony_ci */ 12798c2ecf20Sopenharmony_ciint nfs4_schedule_migration_recovery(const struct nfs_server *server) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci if (server->fh_expire_type != NFS4_FH_PERSISTENT) { 12848c2ecf20Sopenharmony_ci pr_err("NFS: volatile file handles not supported (server %s)\n", 12858c2ecf20Sopenharmony_ci clp->cl_hostname); 12868c2ecf20Sopenharmony_ci return -NFS4ERR_IO; 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (test_bit(NFS_MIG_FAILED, &server->mig_status)) 12908c2ecf20Sopenharmony_ci return -NFS4ERR_IO; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci dprintk("%s: scheduling migration recovery for (%llx:%llx) on %s\n", 12938c2ecf20Sopenharmony_ci __func__, 12948c2ecf20Sopenharmony_ci (unsigned long long)server->fsid.major, 12958c2ecf20Sopenharmony_ci (unsigned long long)server->fsid.minor, 12968c2ecf20Sopenharmony_ci clp->cl_hostname); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci set_bit(NFS_MIG_IN_TRANSITION, 12998c2ecf20Sopenharmony_ci &((struct nfs_server *)server)->mig_status); 13008c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_MOVED, &clp->cl_state); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 13038c2ecf20Sopenharmony_ci return 0; 13048c2ecf20Sopenharmony_ci} 13058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_schedule_migration_recovery); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci/** 13088c2ecf20Sopenharmony_ci * nfs4_schedule_lease_moved_recovery - start lease-moved recovery 13098c2ecf20Sopenharmony_ci * 13108c2ecf20Sopenharmony_ci * @clp: server to check for moved leases 13118c2ecf20Sopenharmony_ci * 13128c2ecf20Sopenharmony_ci */ 13138c2ecf20Sopenharmony_civoid nfs4_schedule_lease_moved_recovery(struct nfs_client *clp) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci dprintk("%s: scheduling lease-moved recovery for client ID %llx on %s\n", 13168c2ecf20Sopenharmony_ci __func__, clp->cl_clientid, clp->cl_hostname); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state); 13198c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 13208c2ecf20Sopenharmony_ci} 13218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_schedule_lease_moved_recovery); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ciint nfs4_wait_clnt_recover(struct nfs_client *clp) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci int res; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci might_sleep(); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci refcount_inc(&clp->cl_count); 13308c2ecf20Sopenharmony_ci res = wait_on_bit_action(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING, 13318c2ecf20Sopenharmony_ci nfs_wait_bit_killable, TASK_KILLABLE); 13328c2ecf20Sopenharmony_ci if (res) 13338c2ecf20Sopenharmony_ci goto out; 13348c2ecf20Sopenharmony_ci if (clp->cl_cons_state < 0) 13358c2ecf20Sopenharmony_ci res = clp->cl_cons_state; 13368c2ecf20Sopenharmony_ciout: 13378c2ecf20Sopenharmony_ci nfs_put_client(clp); 13388c2ecf20Sopenharmony_ci return res; 13398c2ecf20Sopenharmony_ci} 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ciint nfs4_client_recover_expired_lease(struct nfs_client *clp) 13428c2ecf20Sopenharmony_ci{ 13438c2ecf20Sopenharmony_ci unsigned int loop; 13448c2ecf20Sopenharmony_ci int ret; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) { 13478c2ecf20Sopenharmony_ci ret = nfs4_wait_clnt_recover(clp); 13488c2ecf20Sopenharmony_ci if (ret != 0) 13498c2ecf20Sopenharmony_ci break; 13508c2ecf20Sopenharmony_ci if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) && 13518c2ecf20Sopenharmony_ci !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state)) 13528c2ecf20Sopenharmony_ci break; 13538c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 13548c2ecf20Sopenharmony_ci ret = -EIO; 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci return ret; 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci/* 13608c2ecf20Sopenharmony_ci * nfs40_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN 13618c2ecf20Sopenharmony_ci * @clp: client to process 13628c2ecf20Sopenharmony_ci * 13638c2ecf20Sopenharmony_ci * Set the NFS4CLNT_LEASE_EXPIRED state in order to force a 13648c2ecf20Sopenharmony_ci * resend of the SETCLIENTID and hence re-establish the 13658c2ecf20Sopenharmony_ci * callback channel. Then return all existing delegations. 13668c2ecf20Sopenharmony_ci */ 13678c2ecf20Sopenharmony_cistatic void nfs40_handle_cb_pathdown(struct nfs_client *clp) 13688c2ecf20Sopenharmony_ci{ 13698c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); 13708c2ecf20Sopenharmony_ci nfs_expire_all_delegations(clp); 13718c2ecf20Sopenharmony_ci dprintk("%s: handling CB_PATHDOWN recovery for server %s\n", __func__, 13728c2ecf20Sopenharmony_ci clp->cl_hostname); 13738c2ecf20Sopenharmony_ci} 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_civoid nfs4_schedule_path_down_recovery(struct nfs_client *clp) 13768c2ecf20Sopenharmony_ci{ 13778c2ecf20Sopenharmony_ci nfs40_handle_cb_pathdown(clp); 13788c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 13798c2ecf20Sopenharmony_ci} 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_cistatic int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci if (!nfs4_valid_open_stateid(state)) 13858c2ecf20Sopenharmony_ci return 0; 13868c2ecf20Sopenharmony_ci set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); 13878c2ecf20Sopenharmony_ci /* Don't recover state that expired before the reboot */ 13888c2ecf20Sopenharmony_ci if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) { 13898c2ecf20Sopenharmony_ci clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); 13908c2ecf20Sopenharmony_ci return 0; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags); 13938c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state); 13948c2ecf20Sopenharmony_ci return 1; 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ciint nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state) 13988c2ecf20Sopenharmony_ci{ 13998c2ecf20Sopenharmony_ci if (!nfs4_valid_open_stateid(state)) 14008c2ecf20Sopenharmony_ci return 0; 14018c2ecf20Sopenharmony_ci set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags); 14028c2ecf20Sopenharmony_ci clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); 14038c2ecf20Sopenharmony_ci set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags); 14048c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state); 14058c2ecf20Sopenharmony_ci return 1; 14068c2ecf20Sopenharmony_ci} 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ciint nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state) 14098c2ecf20Sopenharmony_ci{ 14108c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci if (!nfs4_state_mark_reclaim_nograce(clp, state)) 14138c2ecf20Sopenharmony_ci return -EBADF; 14148c2ecf20Sopenharmony_ci nfs_inode_find_delegation_state_and_recover(state->inode, 14158c2ecf20Sopenharmony_ci &state->stateid); 14168c2ecf20Sopenharmony_ci dprintk("%s: scheduling stateid recovery for server %s\n", __func__, 14178c2ecf20Sopenharmony_ci clp->cl_hostname); 14188c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 14198c2ecf20Sopenharmony_ci return 0; 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_cistatic struct nfs4_lock_state * 14248c2ecf20Sopenharmony_cinfs_state_find_lock_state_by_stateid(struct nfs4_state *state, 14258c2ecf20Sopenharmony_ci const nfs4_stateid *stateid) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci struct nfs4_lock_state *pos; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci list_for_each_entry(pos, &state->lock_states, ls_locks) { 14308c2ecf20Sopenharmony_ci if (!test_bit(NFS_LOCK_INITIALIZED, &pos->ls_flags)) 14318c2ecf20Sopenharmony_ci continue; 14328c2ecf20Sopenharmony_ci if (nfs4_stateid_match_or_older(&pos->ls_stateid, stateid)) 14338c2ecf20Sopenharmony_ci return pos; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci return NULL; 14368c2ecf20Sopenharmony_ci} 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_cistatic bool nfs_state_lock_state_matches_stateid(struct nfs4_state *state, 14398c2ecf20Sopenharmony_ci const nfs4_stateid *stateid) 14408c2ecf20Sopenharmony_ci{ 14418c2ecf20Sopenharmony_ci bool found = false; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci if (test_bit(LK_STATE_IN_USE, &state->flags)) { 14448c2ecf20Sopenharmony_ci spin_lock(&state->state_lock); 14458c2ecf20Sopenharmony_ci if (nfs_state_find_lock_state_by_stateid(state, stateid)) 14468c2ecf20Sopenharmony_ci found = true; 14478c2ecf20Sopenharmony_ci spin_unlock(&state->state_lock); 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci return found; 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_civoid nfs_inode_find_state_and_recover(struct inode *inode, 14538c2ecf20Sopenharmony_ci const nfs4_stateid *stateid) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 14568c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 14578c2ecf20Sopenharmony_ci struct nfs_open_context *ctx; 14588c2ecf20Sopenharmony_ci struct nfs4_state *state; 14598c2ecf20Sopenharmony_ci bool found = false; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci rcu_read_lock(); 14628c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ctx, &nfsi->open_files, list) { 14638c2ecf20Sopenharmony_ci state = ctx->state; 14648c2ecf20Sopenharmony_ci if (state == NULL) 14658c2ecf20Sopenharmony_ci continue; 14668c2ecf20Sopenharmony_ci if (nfs4_stateid_match_or_older(&state->stateid, stateid) && 14678c2ecf20Sopenharmony_ci nfs4_state_mark_reclaim_nograce(clp, state)) { 14688c2ecf20Sopenharmony_ci found = true; 14698c2ecf20Sopenharmony_ci continue; 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci if (test_bit(NFS_OPEN_STATE, &state->flags) && 14728c2ecf20Sopenharmony_ci nfs4_stateid_match_or_older(&state->open_stateid, stateid) && 14738c2ecf20Sopenharmony_ci nfs4_state_mark_reclaim_nograce(clp, state)) { 14748c2ecf20Sopenharmony_ci found = true; 14758c2ecf20Sopenharmony_ci continue; 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci if (nfs_state_lock_state_matches_stateid(state, stateid) && 14788c2ecf20Sopenharmony_ci nfs4_state_mark_reclaim_nograce(clp, state)) 14798c2ecf20Sopenharmony_ci found = true; 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci rcu_read_unlock(); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci nfs_inode_find_delegation_state_and_recover(inode, stateid); 14848c2ecf20Sopenharmony_ci if (found) 14858c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 14868c2ecf20Sopenharmony_ci} 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_cistatic void nfs4_state_mark_open_context_bad(struct nfs4_state *state, int err) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci struct inode *inode = state->inode; 14918c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 14928c2ecf20Sopenharmony_ci struct nfs_open_context *ctx; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci rcu_read_lock(); 14958c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ctx, &nfsi->open_files, list) { 14968c2ecf20Sopenharmony_ci if (ctx->state != state) 14978c2ecf20Sopenharmony_ci continue; 14988c2ecf20Sopenharmony_ci set_bit(NFS_CONTEXT_BAD, &ctx->flags); 14998c2ecf20Sopenharmony_ci pr_warn("NFSv4: state recovery failed for open file %pd2, " 15008c2ecf20Sopenharmony_ci "error = %d\n", ctx->dentry, err); 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci rcu_read_unlock(); 15038c2ecf20Sopenharmony_ci} 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_cistatic void nfs4_state_mark_recovery_failed(struct nfs4_state *state, int error) 15068c2ecf20Sopenharmony_ci{ 15078c2ecf20Sopenharmony_ci set_bit(NFS_STATE_RECOVERY_FAILED, &state->flags); 15088c2ecf20Sopenharmony_ci nfs4_state_mark_open_context_bad(state, error); 15098c2ecf20Sopenharmony_ci} 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_cistatic int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops) 15138c2ecf20Sopenharmony_ci{ 15148c2ecf20Sopenharmony_ci struct inode *inode = state->inode; 15158c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 15168c2ecf20Sopenharmony_ci struct file_lock *fl; 15178c2ecf20Sopenharmony_ci struct nfs4_lock_state *lsp; 15188c2ecf20Sopenharmony_ci int status = 0; 15198c2ecf20Sopenharmony_ci struct file_lock_context *flctx = inode->i_flctx; 15208c2ecf20Sopenharmony_ci struct list_head *list; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci if (flctx == NULL) 15238c2ecf20Sopenharmony_ci return 0; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci list = &flctx->flc_posix; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci /* Guard against delegation returns and new lock/unlock calls */ 15288c2ecf20Sopenharmony_ci down_write(&nfsi->rwsem); 15298c2ecf20Sopenharmony_ci spin_lock(&flctx->flc_lock); 15308c2ecf20Sopenharmony_cirestart: 15318c2ecf20Sopenharmony_ci list_for_each_entry(fl, list, fl_list) { 15328c2ecf20Sopenharmony_ci if (nfs_file_open_context(fl->fl_file)->state != state) 15338c2ecf20Sopenharmony_ci continue; 15348c2ecf20Sopenharmony_ci spin_unlock(&flctx->flc_lock); 15358c2ecf20Sopenharmony_ci status = ops->recover_lock(state, fl); 15368c2ecf20Sopenharmony_ci switch (status) { 15378c2ecf20Sopenharmony_ci case 0: 15388c2ecf20Sopenharmony_ci break; 15398c2ecf20Sopenharmony_ci case -ETIMEDOUT: 15408c2ecf20Sopenharmony_ci case -ESTALE: 15418c2ecf20Sopenharmony_ci case -NFS4ERR_ADMIN_REVOKED: 15428c2ecf20Sopenharmony_ci case -NFS4ERR_STALE_STATEID: 15438c2ecf20Sopenharmony_ci case -NFS4ERR_BAD_STATEID: 15448c2ecf20Sopenharmony_ci case -NFS4ERR_EXPIRED: 15458c2ecf20Sopenharmony_ci case -NFS4ERR_NO_GRACE: 15468c2ecf20Sopenharmony_ci case -NFS4ERR_STALE_CLIENTID: 15478c2ecf20Sopenharmony_ci case -NFS4ERR_BADSESSION: 15488c2ecf20Sopenharmony_ci case -NFS4ERR_BADSLOT: 15498c2ecf20Sopenharmony_ci case -NFS4ERR_BAD_HIGH_SLOT: 15508c2ecf20Sopenharmony_ci case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: 15518c2ecf20Sopenharmony_ci goto out; 15528c2ecf20Sopenharmony_ci default: 15538c2ecf20Sopenharmony_ci pr_err("NFS: %s: unhandled error %d\n", 15548c2ecf20Sopenharmony_ci __func__, status); 15558c2ecf20Sopenharmony_ci fallthrough; 15568c2ecf20Sopenharmony_ci case -ENOMEM: 15578c2ecf20Sopenharmony_ci case -NFS4ERR_DENIED: 15588c2ecf20Sopenharmony_ci case -NFS4ERR_RECLAIM_BAD: 15598c2ecf20Sopenharmony_ci case -NFS4ERR_RECLAIM_CONFLICT: 15608c2ecf20Sopenharmony_ci lsp = fl->fl_u.nfs4_fl.owner; 15618c2ecf20Sopenharmony_ci if (lsp) 15628c2ecf20Sopenharmony_ci set_bit(NFS_LOCK_LOST, &lsp->ls_flags); 15638c2ecf20Sopenharmony_ci status = 0; 15648c2ecf20Sopenharmony_ci } 15658c2ecf20Sopenharmony_ci spin_lock(&flctx->flc_lock); 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci if (list == &flctx->flc_posix) { 15688c2ecf20Sopenharmony_ci list = &flctx->flc_flock; 15698c2ecf20Sopenharmony_ci goto restart; 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci spin_unlock(&flctx->flc_lock); 15728c2ecf20Sopenharmony_ciout: 15738c2ecf20Sopenharmony_ci up_write(&nfsi->rwsem); 15748c2ecf20Sopenharmony_ci return status; 15758c2ecf20Sopenharmony_ci} 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci#ifdef CONFIG_NFS_V4_2 15788c2ecf20Sopenharmony_cistatic void nfs42_complete_copies(struct nfs4_state_owner *sp, struct nfs4_state *state) 15798c2ecf20Sopenharmony_ci{ 15808c2ecf20Sopenharmony_ci struct nfs4_copy_state *copy; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci if (!test_bit(NFS_CLNT_DST_SSC_COPY_STATE, &state->flags) && 15838c2ecf20Sopenharmony_ci !test_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags)) 15848c2ecf20Sopenharmony_ci return; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci spin_lock(&sp->so_server->nfs_client->cl_lock); 15878c2ecf20Sopenharmony_ci list_for_each_entry(copy, &sp->so_server->ss_copies, copies) { 15888c2ecf20Sopenharmony_ci if ((test_bit(NFS_CLNT_DST_SSC_COPY_STATE, &state->flags) && 15898c2ecf20Sopenharmony_ci !nfs4_stateid_match_other(&state->stateid, 15908c2ecf20Sopenharmony_ci ©->parent_dst_state->stateid))) 15918c2ecf20Sopenharmony_ci continue; 15928c2ecf20Sopenharmony_ci copy->flags = 1; 15938c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS_CLNT_DST_SSC_COPY_STATE, 15948c2ecf20Sopenharmony_ci &state->flags)) { 15958c2ecf20Sopenharmony_ci clear_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags); 15968c2ecf20Sopenharmony_ci complete(©->completion); 15978c2ecf20Sopenharmony_ci } 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci list_for_each_entry(copy, &sp->so_server->ss_copies, src_copies) { 16008c2ecf20Sopenharmony_ci if ((test_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags) && 16018c2ecf20Sopenharmony_ci !nfs4_stateid_match_other(&state->stateid, 16028c2ecf20Sopenharmony_ci ©->parent_src_state->stateid))) 16038c2ecf20Sopenharmony_ci continue; 16048c2ecf20Sopenharmony_ci copy->flags = 1; 16058c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS_CLNT_DST_SSC_COPY_STATE, 16068c2ecf20Sopenharmony_ci &state->flags)) 16078c2ecf20Sopenharmony_ci complete(©->completion); 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci spin_unlock(&sp->so_server->nfs_client->cl_lock); 16108c2ecf20Sopenharmony_ci} 16118c2ecf20Sopenharmony_ci#else /* !CONFIG_NFS_V4_2 */ 16128c2ecf20Sopenharmony_cistatic inline void nfs42_complete_copies(struct nfs4_state_owner *sp, 16138c2ecf20Sopenharmony_ci struct nfs4_state *state) 16148c2ecf20Sopenharmony_ci{ 16158c2ecf20Sopenharmony_ci} 16168c2ecf20Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */ 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_cistatic int __nfs4_reclaim_open_state(struct nfs4_state_owner *sp, struct nfs4_state *state, 16198c2ecf20Sopenharmony_ci const struct nfs4_state_recovery_ops *ops) 16208c2ecf20Sopenharmony_ci{ 16218c2ecf20Sopenharmony_ci struct nfs4_lock_state *lock; 16228c2ecf20Sopenharmony_ci int status; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci status = ops->recover_open(sp, state); 16258c2ecf20Sopenharmony_ci if (status < 0) 16268c2ecf20Sopenharmony_ci return status; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci status = nfs4_reclaim_locks(state, ops); 16298c2ecf20Sopenharmony_ci if (status < 0) 16308c2ecf20Sopenharmony_ci return status; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) { 16338c2ecf20Sopenharmony_ci spin_lock(&state->state_lock); 16348c2ecf20Sopenharmony_ci list_for_each_entry(lock, &state->lock_states, ls_locks) { 16358c2ecf20Sopenharmony_ci trace_nfs4_state_lock_reclaim(state, lock); 16368c2ecf20Sopenharmony_ci if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags)) 16378c2ecf20Sopenharmony_ci pr_warn_ratelimited("NFS: %s: Lock reclaim failed!\n", __func__); 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci spin_unlock(&state->state_lock); 16408c2ecf20Sopenharmony_ci } 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci nfs42_complete_copies(sp, state); 16438c2ecf20Sopenharmony_ci clear_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags); 16448c2ecf20Sopenharmony_ci return status; 16458c2ecf20Sopenharmony_ci} 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_cistatic int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs4_state_recovery_ops *ops) 16488c2ecf20Sopenharmony_ci{ 16498c2ecf20Sopenharmony_ci struct nfs4_state *state; 16508c2ecf20Sopenharmony_ci unsigned int loop = 0; 16518c2ecf20Sopenharmony_ci int status = 0; 16528c2ecf20Sopenharmony_ci#ifdef CONFIG_NFS_V4_2 16538c2ecf20Sopenharmony_ci bool found_ssc_copy_state = false; 16548c2ecf20Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */ 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci /* Note: we rely on the sp->so_states list being ordered 16578c2ecf20Sopenharmony_ci * so that we always reclaim open(O_RDWR) and/or open(O_WRITE) 16588c2ecf20Sopenharmony_ci * states first. 16598c2ecf20Sopenharmony_ci * This is needed to ensure that the server won't give us any 16608c2ecf20Sopenharmony_ci * read delegations that we have to return if, say, we are 16618c2ecf20Sopenharmony_ci * recovering after a network partition or a reboot from a 16628c2ecf20Sopenharmony_ci * server that doesn't support a grace period. 16638c2ecf20Sopenharmony_ci */ 16648c2ecf20Sopenharmony_ci spin_lock(&sp->so_lock); 16658c2ecf20Sopenharmony_ci raw_write_seqcount_begin(&sp->so_reclaim_seqcount); 16668c2ecf20Sopenharmony_cirestart: 16678c2ecf20Sopenharmony_ci list_for_each_entry(state, &sp->so_states, open_states) { 16688c2ecf20Sopenharmony_ci if (!test_and_clear_bit(ops->state_flag_bit, &state->flags)) 16698c2ecf20Sopenharmony_ci continue; 16708c2ecf20Sopenharmony_ci if (!nfs4_valid_open_stateid(state)) 16718c2ecf20Sopenharmony_ci continue; 16728c2ecf20Sopenharmony_ci if (state->state == 0) 16738c2ecf20Sopenharmony_ci continue; 16748c2ecf20Sopenharmony_ci#ifdef CONFIG_NFS_V4_2 16758c2ecf20Sopenharmony_ci if (test_bit(NFS_SRV_SSC_COPY_STATE, &state->flags)) { 16768c2ecf20Sopenharmony_ci nfs4_state_mark_recovery_failed(state, -EIO); 16778c2ecf20Sopenharmony_ci found_ssc_copy_state = true; 16788c2ecf20Sopenharmony_ci continue; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */ 16818c2ecf20Sopenharmony_ci refcount_inc(&state->count); 16828c2ecf20Sopenharmony_ci spin_unlock(&sp->so_lock); 16838c2ecf20Sopenharmony_ci status = __nfs4_reclaim_open_state(sp, state, ops); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci switch (status) { 16868c2ecf20Sopenharmony_ci default: 16878c2ecf20Sopenharmony_ci if (status >= 0) { 16888c2ecf20Sopenharmony_ci loop = 0; 16898c2ecf20Sopenharmony_ci break; 16908c2ecf20Sopenharmony_ci } 16918c2ecf20Sopenharmony_ci printk(KERN_ERR "NFS: %s: unhandled error %d\n", __func__, status); 16928c2ecf20Sopenharmony_ci fallthrough; 16938c2ecf20Sopenharmony_ci case -ENOENT: 16948c2ecf20Sopenharmony_ci case -ENOMEM: 16958c2ecf20Sopenharmony_ci case -EACCES: 16968c2ecf20Sopenharmony_ci case -EROFS: 16978c2ecf20Sopenharmony_ci case -EIO: 16988c2ecf20Sopenharmony_ci case -ESTALE: 16998c2ecf20Sopenharmony_ci /* Open state on this file cannot be recovered */ 17008c2ecf20Sopenharmony_ci nfs4_state_mark_recovery_failed(state, status); 17018c2ecf20Sopenharmony_ci break; 17028c2ecf20Sopenharmony_ci case -EAGAIN: 17038c2ecf20Sopenharmony_ci ssleep(1); 17048c2ecf20Sopenharmony_ci if (loop++ < 10) { 17058c2ecf20Sopenharmony_ci set_bit(ops->state_flag_bit, &state->flags); 17068c2ecf20Sopenharmony_ci break; 17078c2ecf20Sopenharmony_ci } 17088c2ecf20Sopenharmony_ci fallthrough; 17098c2ecf20Sopenharmony_ci case -NFS4ERR_ADMIN_REVOKED: 17108c2ecf20Sopenharmony_ci case -NFS4ERR_STALE_STATEID: 17118c2ecf20Sopenharmony_ci case -NFS4ERR_OLD_STATEID: 17128c2ecf20Sopenharmony_ci case -NFS4ERR_BAD_STATEID: 17138c2ecf20Sopenharmony_ci case -NFS4ERR_RECLAIM_BAD: 17148c2ecf20Sopenharmony_ci case -NFS4ERR_RECLAIM_CONFLICT: 17158c2ecf20Sopenharmony_ci nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state); 17168c2ecf20Sopenharmony_ci break; 17178c2ecf20Sopenharmony_ci case -NFS4ERR_EXPIRED: 17188c2ecf20Sopenharmony_ci case -NFS4ERR_NO_GRACE: 17198c2ecf20Sopenharmony_ci nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state); 17208c2ecf20Sopenharmony_ci fallthrough; 17218c2ecf20Sopenharmony_ci case -NFS4ERR_STALE_CLIENTID: 17228c2ecf20Sopenharmony_ci case -NFS4ERR_BADSESSION: 17238c2ecf20Sopenharmony_ci case -NFS4ERR_BADSLOT: 17248c2ecf20Sopenharmony_ci case -NFS4ERR_BAD_HIGH_SLOT: 17258c2ecf20Sopenharmony_ci case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: 17268c2ecf20Sopenharmony_ci case -ETIMEDOUT: 17278c2ecf20Sopenharmony_ci goto out_err; 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci nfs4_put_open_state(state); 17308c2ecf20Sopenharmony_ci spin_lock(&sp->so_lock); 17318c2ecf20Sopenharmony_ci goto restart; 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci raw_write_seqcount_end(&sp->so_reclaim_seqcount); 17348c2ecf20Sopenharmony_ci spin_unlock(&sp->so_lock); 17358c2ecf20Sopenharmony_ci#ifdef CONFIG_NFS_V4_2 17368c2ecf20Sopenharmony_ci if (found_ssc_copy_state) 17378c2ecf20Sopenharmony_ci return -EIO; 17388c2ecf20Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */ 17398c2ecf20Sopenharmony_ci return 0; 17408c2ecf20Sopenharmony_ciout_err: 17418c2ecf20Sopenharmony_ci nfs4_put_open_state(state); 17428c2ecf20Sopenharmony_ci spin_lock(&sp->so_lock); 17438c2ecf20Sopenharmony_ci raw_write_seqcount_end(&sp->so_reclaim_seqcount); 17448c2ecf20Sopenharmony_ci spin_unlock(&sp->so_lock); 17458c2ecf20Sopenharmony_ci return status; 17468c2ecf20Sopenharmony_ci} 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_cistatic void nfs4_clear_open_state(struct nfs4_state *state) 17498c2ecf20Sopenharmony_ci{ 17508c2ecf20Sopenharmony_ci struct nfs4_lock_state *lock; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATED_STATE, &state->flags); 17538c2ecf20Sopenharmony_ci clear_bit(NFS_O_RDONLY_STATE, &state->flags); 17548c2ecf20Sopenharmony_ci clear_bit(NFS_O_WRONLY_STATE, &state->flags); 17558c2ecf20Sopenharmony_ci clear_bit(NFS_O_RDWR_STATE, &state->flags); 17568c2ecf20Sopenharmony_ci spin_lock(&state->state_lock); 17578c2ecf20Sopenharmony_ci list_for_each_entry(lock, &state->lock_states, ls_locks) { 17588c2ecf20Sopenharmony_ci lock->ls_seqid.flags = 0; 17598c2ecf20Sopenharmony_ci clear_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags); 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci spin_unlock(&state->state_lock); 17628c2ecf20Sopenharmony_ci} 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_cistatic void nfs4_reset_seqids(struct nfs_server *server, 17658c2ecf20Sopenharmony_ci int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state)) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 17688c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp; 17698c2ecf20Sopenharmony_ci struct rb_node *pos; 17708c2ecf20Sopenharmony_ci struct nfs4_state *state; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 17738c2ecf20Sopenharmony_ci for (pos = rb_first(&server->state_owners); 17748c2ecf20Sopenharmony_ci pos != NULL; 17758c2ecf20Sopenharmony_ci pos = rb_next(pos)) { 17768c2ecf20Sopenharmony_ci sp = rb_entry(pos, struct nfs4_state_owner, so_server_node); 17778c2ecf20Sopenharmony_ci sp->so_seqid.flags = 0; 17788c2ecf20Sopenharmony_ci spin_lock(&sp->so_lock); 17798c2ecf20Sopenharmony_ci list_for_each_entry(state, &sp->so_states, open_states) { 17808c2ecf20Sopenharmony_ci if (mark_reclaim(clp, state)) 17818c2ecf20Sopenharmony_ci nfs4_clear_open_state(state); 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci spin_unlock(&sp->so_lock); 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 17868c2ecf20Sopenharmony_ci} 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_cistatic void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, 17898c2ecf20Sopenharmony_ci int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state)) 17908c2ecf20Sopenharmony_ci{ 17918c2ecf20Sopenharmony_ci struct nfs_server *server; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci rcu_read_lock(); 17948c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 17958c2ecf20Sopenharmony_ci nfs4_reset_seqids(server, mark_reclaim); 17968c2ecf20Sopenharmony_ci rcu_read_unlock(); 17978c2ecf20Sopenharmony_ci} 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_cistatic void nfs4_state_start_reclaim_reboot(struct nfs_client *clp) 18008c2ecf20Sopenharmony_ci{ 18018c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state); 18028c2ecf20Sopenharmony_ci /* Mark all delegations for reclaim */ 18038c2ecf20Sopenharmony_ci nfs_delegation_mark_reclaim(clp); 18048c2ecf20Sopenharmony_ci nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot); 18058c2ecf20Sopenharmony_ci} 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_cistatic int nfs4_reclaim_complete(struct nfs_client *clp, 18088c2ecf20Sopenharmony_ci const struct nfs4_state_recovery_ops *ops, 18098c2ecf20Sopenharmony_ci const struct cred *cred) 18108c2ecf20Sopenharmony_ci{ 18118c2ecf20Sopenharmony_ci /* Notify the server we're done reclaiming our state */ 18128c2ecf20Sopenharmony_ci if (ops->reclaim_complete) 18138c2ecf20Sopenharmony_ci return ops->reclaim_complete(clp, cred); 18148c2ecf20Sopenharmony_ci return 0; 18158c2ecf20Sopenharmony_ci} 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_cistatic void nfs4_clear_reclaim_server(struct nfs_server *server) 18188c2ecf20Sopenharmony_ci{ 18198c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 18208c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp; 18218c2ecf20Sopenharmony_ci struct rb_node *pos; 18228c2ecf20Sopenharmony_ci struct nfs4_state *state; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 18258c2ecf20Sopenharmony_ci for (pos = rb_first(&server->state_owners); 18268c2ecf20Sopenharmony_ci pos != NULL; 18278c2ecf20Sopenharmony_ci pos = rb_next(pos)) { 18288c2ecf20Sopenharmony_ci sp = rb_entry(pos, struct nfs4_state_owner, so_server_node); 18298c2ecf20Sopenharmony_ci spin_lock(&sp->so_lock); 18308c2ecf20Sopenharmony_ci list_for_each_entry(state, &sp->so_states, open_states) { 18318c2ecf20Sopenharmony_ci if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT, 18328c2ecf20Sopenharmony_ci &state->flags)) 18338c2ecf20Sopenharmony_ci continue; 18348c2ecf20Sopenharmony_ci nfs4_state_mark_reclaim_nograce(clp, state); 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci spin_unlock(&sp->so_lock); 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 18398c2ecf20Sopenharmony_ci} 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_cistatic int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp) 18428c2ecf20Sopenharmony_ci{ 18438c2ecf20Sopenharmony_ci struct nfs_server *server; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) 18468c2ecf20Sopenharmony_ci return 0; 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci rcu_read_lock(); 18498c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 18508c2ecf20Sopenharmony_ci nfs4_clear_reclaim_server(server); 18518c2ecf20Sopenharmony_ci rcu_read_unlock(); 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci nfs_delegation_reap_unclaimed(clp); 18548c2ecf20Sopenharmony_ci return 1; 18558c2ecf20Sopenharmony_ci} 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_cistatic void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) 18588c2ecf20Sopenharmony_ci{ 18598c2ecf20Sopenharmony_ci const struct nfs4_state_recovery_ops *ops; 18608c2ecf20Sopenharmony_ci const struct cred *cred; 18618c2ecf20Sopenharmony_ci int err; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci if (!nfs4_state_clear_reclaim_reboot(clp)) 18648c2ecf20Sopenharmony_ci return; 18658c2ecf20Sopenharmony_ci ops = clp->cl_mvops->reboot_recovery_ops; 18668c2ecf20Sopenharmony_ci cred = nfs4_get_clid_cred(clp); 18678c2ecf20Sopenharmony_ci err = nfs4_reclaim_complete(clp, ops, cred); 18688c2ecf20Sopenharmony_ci put_cred(cred); 18698c2ecf20Sopenharmony_ci if (err == -NFS4ERR_CONN_NOT_BOUND_TO_SESSION) 18708c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state); 18718c2ecf20Sopenharmony_ci} 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_cistatic void nfs4_state_start_reclaim_nograce(struct nfs_client *clp) 18748c2ecf20Sopenharmony_ci{ 18758c2ecf20Sopenharmony_ci nfs_mark_test_expired_all_delegations(clp); 18768c2ecf20Sopenharmony_ci nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); 18778c2ecf20Sopenharmony_ci} 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_cistatic int nfs4_recovery_handle_error(struct nfs_client *clp, int error) 18808c2ecf20Sopenharmony_ci{ 18818c2ecf20Sopenharmony_ci switch (error) { 18828c2ecf20Sopenharmony_ci case 0: 18838c2ecf20Sopenharmony_ci break; 18848c2ecf20Sopenharmony_ci case -NFS4ERR_CB_PATH_DOWN: 18858c2ecf20Sopenharmony_ci nfs40_handle_cb_pathdown(clp); 18868c2ecf20Sopenharmony_ci break; 18878c2ecf20Sopenharmony_ci case -NFS4ERR_NO_GRACE: 18888c2ecf20Sopenharmony_ci nfs4_state_end_reclaim_reboot(clp); 18898c2ecf20Sopenharmony_ci break; 18908c2ecf20Sopenharmony_ci case -NFS4ERR_STALE_CLIENTID: 18918c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); 18928c2ecf20Sopenharmony_ci nfs4_state_start_reclaim_reboot(clp); 18938c2ecf20Sopenharmony_ci break; 18948c2ecf20Sopenharmony_ci case -NFS4ERR_EXPIRED: 18958c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); 18968c2ecf20Sopenharmony_ci nfs4_state_start_reclaim_nograce(clp); 18978c2ecf20Sopenharmony_ci break; 18988c2ecf20Sopenharmony_ci case -NFS4ERR_BADSESSION: 18998c2ecf20Sopenharmony_ci case -NFS4ERR_BADSLOT: 19008c2ecf20Sopenharmony_ci case -NFS4ERR_BAD_HIGH_SLOT: 19018c2ecf20Sopenharmony_ci case -NFS4ERR_DEADSESSION: 19028c2ecf20Sopenharmony_ci case -NFS4ERR_SEQ_FALSE_RETRY: 19038c2ecf20Sopenharmony_ci case -NFS4ERR_SEQ_MISORDERED: 19048c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); 19058c2ecf20Sopenharmony_ci /* Zero session reset errors */ 19068c2ecf20Sopenharmony_ci break; 19078c2ecf20Sopenharmony_ci case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: 19088c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state); 19098c2ecf20Sopenharmony_ci break; 19108c2ecf20Sopenharmony_ci default: 19118c2ecf20Sopenharmony_ci dprintk("%s: failed to handle error %d for server %s\n", 19128c2ecf20Sopenharmony_ci __func__, error, clp->cl_hostname); 19138c2ecf20Sopenharmony_ci return error; 19148c2ecf20Sopenharmony_ci } 19158c2ecf20Sopenharmony_ci dprintk("%s: handled error %d for server %s\n", __func__, error, 19168c2ecf20Sopenharmony_ci clp->cl_hostname); 19178c2ecf20Sopenharmony_ci return 0; 19188c2ecf20Sopenharmony_ci} 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_cistatic int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops) 19218c2ecf20Sopenharmony_ci{ 19228c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp; 19238c2ecf20Sopenharmony_ci struct nfs_server *server; 19248c2ecf20Sopenharmony_ci struct rb_node *pos; 19258c2ecf20Sopenharmony_ci LIST_HEAD(freeme); 19268c2ecf20Sopenharmony_ci int status = 0; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_cirestart: 19298c2ecf20Sopenharmony_ci rcu_read_lock(); 19308c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 19318c2ecf20Sopenharmony_ci nfs4_purge_state_owners(server, &freeme); 19328c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 19338c2ecf20Sopenharmony_ci for (pos = rb_first(&server->state_owners); 19348c2ecf20Sopenharmony_ci pos != NULL; 19358c2ecf20Sopenharmony_ci pos = rb_next(pos)) { 19368c2ecf20Sopenharmony_ci sp = rb_entry(pos, 19378c2ecf20Sopenharmony_ci struct nfs4_state_owner, so_server_node); 19388c2ecf20Sopenharmony_ci if (!test_and_clear_bit(ops->owner_flag_bit, 19398c2ecf20Sopenharmony_ci &sp->so_flags)) 19408c2ecf20Sopenharmony_ci continue; 19418c2ecf20Sopenharmony_ci if (!atomic_inc_not_zero(&sp->so_count)) 19428c2ecf20Sopenharmony_ci continue; 19438c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 19448c2ecf20Sopenharmony_ci rcu_read_unlock(); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci status = nfs4_reclaim_open_state(sp, ops); 19478c2ecf20Sopenharmony_ci if (status < 0) { 19488c2ecf20Sopenharmony_ci set_bit(ops->owner_flag_bit, &sp->so_flags); 19498c2ecf20Sopenharmony_ci nfs4_put_state_owner(sp); 19508c2ecf20Sopenharmony_ci status = nfs4_recovery_handle_error(clp, status); 19518c2ecf20Sopenharmony_ci return (status != 0) ? status : -EAGAIN; 19528c2ecf20Sopenharmony_ci } 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci nfs4_put_state_owner(sp); 19558c2ecf20Sopenharmony_ci goto restart; 19568c2ecf20Sopenharmony_ci } 19578c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 19588c2ecf20Sopenharmony_ci } 19598c2ecf20Sopenharmony_ci rcu_read_unlock(); 19608c2ecf20Sopenharmony_ci nfs4_free_state_owners(&freeme); 19618c2ecf20Sopenharmony_ci return 0; 19628c2ecf20Sopenharmony_ci} 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_cistatic int nfs4_check_lease(struct nfs_client *clp) 19658c2ecf20Sopenharmony_ci{ 19668c2ecf20Sopenharmony_ci const struct cred *cred; 19678c2ecf20Sopenharmony_ci const struct nfs4_state_maintenance_ops *ops = 19688c2ecf20Sopenharmony_ci clp->cl_mvops->state_renewal_ops; 19698c2ecf20Sopenharmony_ci int status; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci /* Is the client already known to have an expired lease? */ 19728c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) 19738c2ecf20Sopenharmony_ci return 0; 19748c2ecf20Sopenharmony_ci cred = ops->get_state_renewal_cred(clp); 19758c2ecf20Sopenharmony_ci if (cred == NULL) { 19768c2ecf20Sopenharmony_ci cred = nfs4_get_clid_cred(clp); 19778c2ecf20Sopenharmony_ci status = -ENOKEY; 19788c2ecf20Sopenharmony_ci if (cred == NULL) 19798c2ecf20Sopenharmony_ci goto out; 19808c2ecf20Sopenharmony_ci } 19818c2ecf20Sopenharmony_ci status = ops->renew_lease(clp, cred); 19828c2ecf20Sopenharmony_ci put_cred(cred); 19838c2ecf20Sopenharmony_ci if (status == -ETIMEDOUT) { 19848c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); 19858c2ecf20Sopenharmony_ci return 0; 19868c2ecf20Sopenharmony_ci } 19878c2ecf20Sopenharmony_ciout: 19888c2ecf20Sopenharmony_ci return nfs4_recovery_handle_error(clp, status); 19898c2ecf20Sopenharmony_ci} 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci/* Set NFS4CLNT_LEASE_EXPIRED and reclaim reboot state for all v4.0 errors 19928c2ecf20Sopenharmony_ci * and for recoverable errors on EXCHANGE_ID for v4.1 19938c2ecf20Sopenharmony_ci */ 19948c2ecf20Sopenharmony_cistatic int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status) 19958c2ecf20Sopenharmony_ci{ 19968c2ecf20Sopenharmony_ci switch (status) { 19978c2ecf20Sopenharmony_ci case -NFS4ERR_SEQ_MISORDERED: 19988c2ecf20Sopenharmony_ci if (test_and_set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) 19998c2ecf20Sopenharmony_ci return -ESERVERFAULT; 20008c2ecf20Sopenharmony_ci /* Lease confirmation error: retry after purging the lease */ 20018c2ecf20Sopenharmony_ci ssleep(1); 20028c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); 20038c2ecf20Sopenharmony_ci break; 20048c2ecf20Sopenharmony_ci case -NFS4ERR_STALE_CLIENTID: 20058c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); 20068c2ecf20Sopenharmony_ci nfs4_state_start_reclaim_reboot(clp); 20078c2ecf20Sopenharmony_ci break; 20088c2ecf20Sopenharmony_ci case -NFS4ERR_CLID_INUSE: 20098c2ecf20Sopenharmony_ci pr_err("NFS: Server %s reports our clientid is in use\n", 20108c2ecf20Sopenharmony_ci clp->cl_hostname); 20118c2ecf20Sopenharmony_ci nfs_mark_client_ready(clp, -EPERM); 20128c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); 20138c2ecf20Sopenharmony_ci return -EPERM; 20148c2ecf20Sopenharmony_ci case -EACCES: 20158c2ecf20Sopenharmony_ci case -NFS4ERR_DELAY: 20168c2ecf20Sopenharmony_ci case -EAGAIN: 20178c2ecf20Sopenharmony_ci ssleep(1); 20188c2ecf20Sopenharmony_ci break; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci case -NFS4ERR_MINOR_VERS_MISMATCH: 20218c2ecf20Sopenharmony_ci if (clp->cl_cons_state == NFS_CS_SESSION_INITING) 20228c2ecf20Sopenharmony_ci nfs_mark_client_ready(clp, -EPROTONOSUPPORT); 20238c2ecf20Sopenharmony_ci dprintk("%s: exit with error %d for server %s\n", 20248c2ecf20Sopenharmony_ci __func__, -EPROTONOSUPPORT, clp->cl_hostname); 20258c2ecf20Sopenharmony_ci return -EPROTONOSUPPORT; 20268c2ecf20Sopenharmony_ci case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery 20278c2ecf20Sopenharmony_ci * in nfs4_exchange_id */ 20288c2ecf20Sopenharmony_ci default: 20298c2ecf20Sopenharmony_ci dprintk("%s: exit with error %d for server %s\n", __func__, 20308c2ecf20Sopenharmony_ci status, clp->cl_hostname); 20318c2ecf20Sopenharmony_ci return status; 20328c2ecf20Sopenharmony_ci } 20338c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); 20348c2ecf20Sopenharmony_ci dprintk("%s: handled error %d for server %s\n", __func__, status, 20358c2ecf20Sopenharmony_ci clp->cl_hostname); 20368c2ecf20Sopenharmony_ci return 0; 20378c2ecf20Sopenharmony_ci} 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_cistatic int nfs4_establish_lease(struct nfs_client *clp) 20408c2ecf20Sopenharmony_ci{ 20418c2ecf20Sopenharmony_ci const struct cred *cred; 20428c2ecf20Sopenharmony_ci const struct nfs4_state_recovery_ops *ops = 20438c2ecf20Sopenharmony_ci clp->cl_mvops->reboot_recovery_ops; 20448c2ecf20Sopenharmony_ci int status; 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci status = nfs4_begin_drain_session(clp); 20478c2ecf20Sopenharmony_ci if (status != 0) 20488c2ecf20Sopenharmony_ci return status; 20498c2ecf20Sopenharmony_ci cred = nfs4_get_clid_cred(clp); 20508c2ecf20Sopenharmony_ci if (cred == NULL) 20518c2ecf20Sopenharmony_ci return -ENOENT; 20528c2ecf20Sopenharmony_ci status = ops->establish_clid(clp, cred); 20538c2ecf20Sopenharmony_ci put_cred(cred); 20548c2ecf20Sopenharmony_ci if (status != 0) 20558c2ecf20Sopenharmony_ci return status; 20568c2ecf20Sopenharmony_ci pnfs_destroy_all_layouts(clp); 20578c2ecf20Sopenharmony_ci return 0; 20588c2ecf20Sopenharmony_ci} 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci/* 20618c2ecf20Sopenharmony_ci * Returns zero or a negative errno. NFS4ERR values are converted 20628c2ecf20Sopenharmony_ci * to local errno values. 20638c2ecf20Sopenharmony_ci */ 20648c2ecf20Sopenharmony_cistatic int nfs4_reclaim_lease(struct nfs_client *clp) 20658c2ecf20Sopenharmony_ci{ 20668c2ecf20Sopenharmony_ci int status; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci status = nfs4_establish_lease(clp); 20698c2ecf20Sopenharmony_ci if (status < 0) 20708c2ecf20Sopenharmony_ci return nfs4_handle_reclaim_lease_error(clp, status); 20718c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state)) 20728c2ecf20Sopenharmony_ci nfs4_state_start_reclaim_nograce(clp); 20738c2ecf20Sopenharmony_ci if (!test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) 20748c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state); 20758c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); 20768c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); 20778c2ecf20Sopenharmony_ci return 0; 20788c2ecf20Sopenharmony_ci} 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_cistatic int nfs4_purge_lease(struct nfs_client *clp) 20818c2ecf20Sopenharmony_ci{ 20828c2ecf20Sopenharmony_ci int status; 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci status = nfs4_establish_lease(clp); 20858c2ecf20Sopenharmony_ci if (status < 0) 20868c2ecf20Sopenharmony_ci return nfs4_handle_reclaim_lease_error(clp, status); 20878c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state); 20888c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); 20898c2ecf20Sopenharmony_ci nfs4_state_start_reclaim_nograce(clp); 20908c2ecf20Sopenharmony_ci return 0; 20918c2ecf20Sopenharmony_ci} 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci/* 20948c2ecf20Sopenharmony_ci * Try remote migration of one FSID from a source server to a 20958c2ecf20Sopenharmony_ci * destination server. The source server provides a list of 20968c2ecf20Sopenharmony_ci * potential destinations. 20978c2ecf20Sopenharmony_ci * 20988c2ecf20Sopenharmony_ci * Returns zero or a negative NFS4ERR status code. 20998c2ecf20Sopenharmony_ci */ 21008c2ecf20Sopenharmony_cistatic int nfs4_try_migration(struct nfs_server *server, const struct cred *cred) 21018c2ecf20Sopenharmony_ci{ 21028c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 21038c2ecf20Sopenharmony_ci struct nfs4_fs_locations *locations = NULL; 21048c2ecf20Sopenharmony_ci struct inode *inode; 21058c2ecf20Sopenharmony_ci struct page *page; 21068c2ecf20Sopenharmony_ci int status, result; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci dprintk("--> %s: FSID %llx:%llx on \"%s\"\n", __func__, 21098c2ecf20Sopenharmony_ci (unsigned long long)server->fsid.major, 21108c2ecf20Sopenharmony_ci (unsigned long long)server->fsid.minor, 21118c2ecf20Sopenharmony_ci clp->cl_hostname); 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci result = 0; 21148c2ecf20Sopenharmony_ci page = alloc_page(GFP_KERNEL); 21158c2ecf20Sopenharmony_ci locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); 21168c2ecf20Sopenharmony_ci if (page == NULL || locations == NULL) { 21178c2ecf20Sopenharmony_ci dprintk("<-- %s: no memory\n", __func__); 21188c2ecf20Sopenharmony_ci goto out; 21198c2ecf20Sopenharmony_ci } 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci inode = d_inode(server->super->s_root); 21228c2ecf20Sopenharmony_ci result = nfs4_proc_get_locations(inode, locations, page, cred); 21238c2ecf20Sopenharmony_ci if (result) { 21248c2ecf20Sopenharmony_ci dprintk("<-- %s: failed to retrieve fs_locations: %d\n", 21258c2ecf20Sopenharmony_ci __func__, result); 21268c2ecf20Sopenharmony_ci goto out; 21278c2ecf20Sopenharmony_ci } 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci result = -NFS4ERR_NXIO; 21308c2ecf20Sopenharmony_ci if (!locations->nlocations) 21318c2ecf20Sopenharmony_ci goto out; 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci if (!(locations->fattr.valid & NFS_ATTR_FATTR_V4_LOCATIONS)) { 21348c2ecf20Sopenharmony_ci dprintk("<-- %s: No fs_locations data, migration skipped\n", 21358c2ecf20Sopenharmony_ci __func__); 21368c2ecf20Sopenharmony_ci goto out; 21378c2ecf20Sopenharmony_ci } 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci status = nfs4_begin_drain_session(clp); 21408c2ecf20Sopenharmony_ci if (status != 0) { 21418c2ecf20Sopenharmony_ci result = status; 21428c2ecf20Sopenharmony_ci goto out; 21438c2ecf20Sopenharmony_ci } 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci status = nfs4_replace_transport(server, locations); 21468c2ecf20Sopenharmony_ci if (status != 0) { 21478c2ecf20Sopenharmony_ci dprintk("<-- %s: failed to replace transport: %d\n", 21488c2ecf20Sopenharmony_ci __func__, status); 21498c2ecf20Sopenharmony_ci goto out; 21508c2ecf20Sopenharmony_ci } 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci result = 0; 21538c2ecf20Sopenharmony_ci dprintk("<-- %s: migration succeeded\n", __func__); 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ciout: 21568c2ecf20Sopenharmony_ci if (page != NULL) 21578c2ecf20Sopenharmony_ci __free_page(page); 21588c2ecf20Sopenharmony_ci kfree(locations); 21598c2ecf20Sopenharmony_ci if (result) { 21608c2ecf20Sopenharmony_ci pr_err("NFS: migration recovery failed (server %s)\n", 21618c2ecf20Sopenharmony_ci clp->cl_hostname); 21628c2ecf20Sopenharmony_ci set_bit(NFS_MIG_FAILED, &server->mig_status); 21638c2ecf20Sopenharmony_ci } 21648c2ecf20Sopenharmony_ci return result; 21658c2ecf20Sopenharmony_ci} 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci/* 21688c2ecf20Sopenharmony_ci * Returns zero or a negative NFS4ERR status code. 21698c2ecf20Sopenharmony_ci */ 21708c2ecf20Sopenharmony_cistatic int nfs4_handle_migration(struct nfs_client *clp) 21718c2ecf20Sopenharmony_ci{ 21728c2ecf20Sopenharmony_ci const struct nfs4_state_maintenance_ops *ops = 21738c2ecf20Sopenharmony_ci clp->cl_mvops->state_renewal_ops; 21748c2ecf20Sopenharmony_ci struct nfs_server *server; 21758c2ecf20Sopenharmony_ci const struct cred *cred; 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci dprintk("%s: migration reported on \"%s\"\n", __func__, 21788c2ecf20Sopenharmony_ci clp->cl_hostname); 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci cred = ops->get_state_renewal_cred(clp); 21818c2ecf20Sopenharmony_ci if (cred == NULL) 21828c2ecf20Sopenharmony_ci return -NFS4ERR_NOENT; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci clp->cl_mig_gen++; 21858c2ecf20Sopenharmony_cirestart: 21868c2ecf20Sopenharmony_ci rcu_read_lock(); 21878c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 21888c2ecf20Sopenharmony_ci int status; 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci if (server->mig_gen == clp->cl_mig_gen) 21918c2ecf20Sopenharmony_ci continue; 21928c2ecf20Sopenharmony_ci server->mig_gen = clp->cl_mig_gen; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci if (!test_and_clear_bit(NFS_MIG_IN_TRANSITION, 21958c2ecf20Sopenharmony_ci &server->mig_status)) 21968c2ecf20Sopenharmony_ci continue; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci rcu_read_unlock(); 21998c2ecf20Sopenharmony_ci status = nfs4_try_migration(server, cred); 22008c2ecf20Sopenharmony_ci if (status < 0) { 22018c2ecf20Sopenharmony_ci put_cred(cred); 22028c2ecf20Sopenharmony_ci return status; 22038c2ecf20Sopenharmony_ci } 22048c2ecf20Sopenharmony_ci goto restart; 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci rcu_read_unlock(); 22078c2ecf20Sopenharmony_ci put_cred(cred); 22088c2ecf20Sopenharmony_ci return 0; 22098c2ecf20Sopenharmony_ci} 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci/* 22128c2ecf20Sopenharmony_ci * Test each nfs_server on the clp's cl_superblocks list to see 22138c2ecf20Sopenharmony_ci * if it's moved to another server. Stop when the server no longer 22148c2ecf20Sopenharmony_ci * returns NFS4ERR_LEASE_MOVED. 22158c2ecf20Sopenharmony_ci */ 22168c2ecf20Sopenharmony_cistatic int nfs4_handle_lease_moved(struct nfs_client *clp) 22178c2ecf20Sopenharmony_ci{ 22188c2ecf20Sopenharmony_ci const struct nfs4_state_maintenance_ops *ops = 22198c2ecf20Sopenharmony_ci clp->cl_mvops->state_renewal_ops; 22208c2ecf20Sopenharmony_ci struct nfs_server *server; 22218c2ecf20Sopenharmony_ci const struct cred *cred; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci dprintk("%s: lease moved reported on \"%s\"\n", __func__, 22248c2ecf20Sopenharmony_ci clp->cl_hostname); 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci cred = ops->get_state_renewal_cred(clp); 22278c2ecf20Sopenharmony_ci if (cred == NULL) 22288c2ecf20Sopenharmony_ci return -NFS4ERR_NOENT; 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci clp->cl_mig_gen++; 22318c2ecf20Sopenharmony_cirestart: 22328c2ecf20Sopenharmony_ci rcu_read_lock(); 22338c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 22348c2ecf20Sopenharmony_ci struct inode *inode; 22358c2ecf20Sopenharmony_ci int status; 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci if (server->mig_gen == clp->cl_mig_gen) 22388c2ecf20Sopenharmony_ci continue; 22398c2ecf20Sopenharmony_ci server->mig_gen = clp->cl_mig_gen; 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci rcu_read_unlock(); 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci inode = d_inode(server->super->s_root); 22448c2ecf20Sopenharmony_ci status = nfs4_proc_fsid_present(inode, cred); 22458c2ecf20Sopenharmony_ci if (status != -NFS4ERR_MOVED) 22468c2ecf20Sopenharmony_ci goto restart; /* wasn't this one */ 22478c2ecf20Sopenharmony_ci if (nfs4_try_migration(server, cred) == -NFS4ERR_LEASE_MOVED) 22488c2ecf20Sopenharmony_ci goto restart; /* there are more */ 22498c2ecf20Sopenharmony_ci goto out; 22508c2ecf20Sopenharmony_ci } 22518c2ecf20Sopenharmony_ci rcu_read_unlock(); 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ciout: 22548c2ecf20Sopenharmony_ci put_cred(cred); 22558c2ecf20Sopenharmony_ci return 0; 22568c2ecf20Sopenharmony_ci} 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci/** 22598c2ecf20Sopenharmony_ci * nfs4_discover_server_trunking - Detect server IP address trunking 22608c2ecf20Sopenharmony_ci * 22618c2ecf20Sopenharmony_ci * @clp: nfs_client under test 22628c2ecf20Sopenharmony_ci * @result: OUT: found nfs_client, or clp 22638c2ecf20Sopenharmony_ci * 22648c2ecf20Sopenharmony_ci * Returns zero or a negative errno. If zero is returned, 22658c2ecf20Sopenharmony_ci * an nfs_client pointer is planted in "result". 22668c2ecf20Sopenharmony_ci * 22678c2ecf20Sopenharmony_ci * Note: since we are invoked in process context, and 22688c2ecf20Sopenharmony_ci * not from inside the state manager, we cannot use 22698c2ecf20Sopenharmony_ci * nfs4_handle_reclaim_lease_error(). 22708c2ecf20Sopenharmony_ci */ 22718c2ecf20Sopenharmony_ciint nfs4_discover_server_trunking(struct nfs_client *clp, 22728c2ecf20Sopenharmony_ci struct nfs_client **result) 22738c2ecf20Sopenharmony_ci{ 22748c2ecf20Sopenharmony_ci const struct nfs4_state_recovery_ops *ops = 22758c2ecf20Sopenharmony_ci clp->cl_mvops->reboot_recovery_ops; 22768c2ecf20Sopenharmony_ci struct rpc_clnt *clnt; 22778c2ecf20Sopenharmony_ci const struct cred *cred; 22788c2ecf20Sopenharmony_ci int i, status; 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname); 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci clnt = clp->cl_rpcclient; 22838c2ecf20Sopenharmony_ci i = 0; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci mutex_lock(&nfs_clid_init_mutex); 22868c2ecf20Sopenharmony_ciagain: 22878c2ecf20Sopenharmony_ci status = -ENOENT; 22888c2ecf20Sopenharmony_ci cred = nfs4_get_clid_cred(clp); 22898c2ecf20Sopenharmony_ci if (cred == NULL) 22908c2ecf20Sopenharmony_ci goto out_unlock; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci status = ops->detect_trunking(clp, result, cred); 22938c2ecf20Sopenharmony_ci put_cred(cred); 22948c2ecf20Sopenharmony_ci switch (status) { 22958c2ecf20Sopenharmony_ci case 0: 22968c2ecf20Sopenharmony_ci case -EINTR: 22978c2ecf20Sopenharmony_ci case -ERESTARTSYS: 22988c2ecf20Sopenharmony_ci break; 22998c2ecf20Sopenharmony_ci case -ETIMEDOUT: 23008c2ecf20Sopenharmony_ci if (clnt->cl_softrtry) 23018c2ecf20Sopenharmony_ci break; 23028c2ecf20Sopenharmony_ci fallthrough; 23038c2ecf20Sopenharmony_ci case -NFS4ERR_DELAY: 23048c2ecf20Sopenharmony_ci case -EAGAIN: 23058c2ecf20Sopenharmony_ci ssleep(1); 23068c2ecf20Sopenharmony_ci fallthrough; 23078c2ecf20Sopenharmony_ci case -NFS4ERR_STALE_CLIENTID: 23088c2ecf20Sopenharmony_ci dprintk("NFS: %s after status %d, retrying\n", 23098c2ecf20Sopenharmony_ci __func__, status); 23108c2ecf20Sopenharmony_ci goto again; 23118c2ecf20Sopenharmony_ci case -EACCES: 23128c2ecf20Sopenharmony_ci if (i++ == 0) { 23138c2ecf20Sopenharmony_ci nfs4_root_machine_cred(clp); 23148c2ecf20Sopenharmony_ci goto again; 23158c2ecf20Sopenharmony_ci } 23168c2ecf20Sopenharmony_ci if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX) 23178c2ecf20Sopenharmony_ci break; 23188c2ecf20Sopenharmony_ci fallthrough; 23198c2ecf20Sopenharmony_ci case -NFS4ERR_CLID_INUSE: 23208c2ecf20Sopenharmony_ci case -NFS4ERR_WRONGSEC: 23218c2ecf20Sopenharmony_ci /* No point in retrying if we already used RPC_AUTH_UNIX */ 23228c2ecf20Sopenharmony_ci if (clnt->cl_auth->au_flavor == RPC_AUTH_UNIX) { 23238c2ecf20Sopenharmony_ci status = -EPERM; 23248c2ecf20Sopenharmony_ci break; 23258c2ecf20Sopenharmony_ci } 23268c2ecf20Sopenharmony_ci clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX); 23278c2ecf20Sopenharmony_ci if (IS_ERR(clnt)) { 23288c2ecf20Sopenharmony_ci status = PTR_ERR(clnt); 23298c2ecf20Sopenharmony_ci break; 23308c2ecf20Sopenharmony_ci } 23318c2ecf20Sopenharmony_ci /* Note: this is safe because we haven't yet marked the 23328c2ecf20Sopenharmony_ci * client as ready, so we are the only user of 23338c2ecf20Sopenharmony_ci * clp->cl_rpcclient 23348c2ecf20Sopenharmony_ci */ 23358c2ecf20Sopenharmony_ci clnt = xchg(&clp->cl_rpcclient, clnt); 23368c2ecf20Sopenharmony_ci rpc_shutdown_client(clnt); 23378c2ecf20Sopenharmony_ci clnt = clp->cl_rpcclient; 23388c2ecf20Sopenharmony_ci goto again; 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci case -NFS4ERR_MINOR_VERS_MISMATCH: 23418c2ecf20Sopenharmony_ci status = -EPROTONOSUPPORT; 23428c2ecf20Sopenharmony_ci break; 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci case -EKEYEXPIRED: 23458c2ecf20Sopenharmony_ci case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery 23468c2ecf20Sopenharmony_ci * in nfs4_exchange_id */ 23478c2ecf20Sopenharmony_ci status = -EKEYEXPIRED; 23488c2ecf20Sopenharmony_ci break; 23498c2ecf20Sopenharmony_ci default: 23508c2ecf20Sopenharmony_ci pr_warn("NFS: %s unhandled error %d. Exiting with error EIO\n", 23518c2ecf20Sopenharmony_ci __func__, status); 23528c2ecf20Sopenharmony_ci status = -EIO; 23538c2ecf20Sopenharmony_ci } 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ciout_unlock: 23568c2ecf20Sopenharmony_ci mutex_unlock(&nfs_clid_init_mutex); 23578c2ecf20Sopenharmony_ci dprintk("NFS: %s: status = %d\n", __func__, status); 23588c2ecf20Sopenharmony_ci return status; 23598c2ecf20Sopenharmony_ci} 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci#ifdef CONFIG_NFS_V4_1 23628c2ecf20Sopenharmony_civoid nfs4_schedule_session_recovery(struct nfs4_session *session, int err) 23638c2ecf20Sopenharmony_ci{ 23648c2ecf20Sopenharmony_ci struct nfs_client *clp = session->clp; 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci switch (err) { 23678c2ecf20Sopenharmony_ci default: 23688c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); 23698c2ecf20Sopenharmony_ci break; 23708c2ecf20Sopenharmony_ci case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: 23718c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state); 23728c2ecf20Sopenharmony_ci } 23738c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 23748c2ecf20Sopenharmony_ci} 23758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery); 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_civoid nfs41_notify_server(struct nfs_client *clp) 23788c2ecf20Sopenharmony_ci{ 23798c2ecf20Sopenharmony_ci /* Use CHECK_LEASE to ping the server with a SEQUENCE */ 23808c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); 23818c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 23828c2ecf20Sopenharmony_ci} 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_cistatic void nfs4_reset_all_state(struct nfs_client *clp) 23858c2ecf20Sopenharmony_ci{ 23868c2ecf20Sopenharmony_ci if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) { 23878c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state); 23888c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); 23898c2ecf20Sopenharmony_ci nfs4_state_start_reclaim_nograce(clp); 23908c2ecf20Sopenharmony_ci dprintk("%s: scheduling reset of all state for server %s!\n", 23918c2ecf20Sopenharmony_ci __func__, clp->cl_hostname); 23928c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 23938c2ecf20Sopenharmony_ci } 23948c2ecf20Sopenharmony_ci} 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_cistatic void nfs41_handle_server_reboot(struct nfs_client *clp) 23978c2ecf20Sopenharmony_ci{ 23988c2ecf20Sopenharmony_ci if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) { 23998c2ecf20Sopenharmony_ci nfs4_state_start_reclaim_reboot(clp); 24008c2ecf20Sopenharmony_ci dprintk("%s: server %s rebooted!\n", __func__, 24018c2ecf20Sopenharmony_ci clp->cl_hostname); 24028c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 24038c2ecf20Sopenharmony_ci } 24048c2ecf20Sopenharmony_ci} 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_cistatic void nfs41_handle_all_state_revoked(struct nfs_client *clp) 24078c2ecf20Sopenharmony_ci{ 24088c2ecf20Sopenharmony_ci nfs4_reset_all_state(clp); 24098c2ecf20Sopenharmony_ci dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname); 24108c2ecf20Sopenharmony_ci} 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_cistatic void nfs41_handle_some_state_revoked(struct nfs_client *clp) 24138c2ecf20Sopenharmony_ci{ 24148c2ecf20Sopenharmony_ci nfs4_state_start_reclaim_nograce(clp); 24158c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname); 24188c2ecf20Sopenharmony_ci} 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_cistatic void nfs41_handle_recallable_state_revoked(struct nfs_client *clp) 24218c2ecf20Sopenharmony_ci{ 24228c2ecf20Sopenharmony_ci /* FIXME: For now, we destroy all layouts. */ 24238c2ecf20Sopenharmony_ci pnfs_destroy_all_layouts(clp); 24248c2ecf20Sopenharmony_ci nfs_test_expired_all_delegations(clp); 24258c2ecf20Sopenharmony_ci dprintk("%s: Recallable state revoked on server %s!\n", __func__, 24268c2ecf20Sopenharmony_ci clp->cl_hostname); 24278c2ecf20Sopenharmony_ci} 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_cistatic void nfs41_handle_backchannel_fault(struct nfs_client *clp) 24308c2ecf20Sopenharmony_ci{ 24318c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); 24328c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci dprintk("%s: server %s declared a backchannel fault\n", __func__, 24358c2ecf20Sopenharmony_ci clp->cl_hostname); 24368c2ecf20Sopenharmony_ci} 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_cistatic void nfs41_handle_cb_path_down(struct nfs_client *clp) 24398c2ecf20Sopenharmony_ci{ 24408c2ecf20Sopenharmony_ci if (test_and_set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, 24418c2ecf20Sopenharmony_ci &clp->cl_state) == 0) 24428c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 24438c2ecf20Sopenharmony_ci} 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_civoid nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags, 24468c2ecf20Sopenharmony_ci bool recovery) 24478c2ecf20Sopenharmony_ci{ 24488c2ecf20Sopenharmony_ci if (!flags) 24498c2ecf20Sopenharmony_ci return; 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci dprintk("%s: \"%s\" (client ID %llx) flags=0x%08x\n", 24528c2ecf20Sopenharmony_ci __func__, clp->cl_hostname, clp->cl_clientid, flags); 24538c2ecf20Sopenharmony_ci /* 24548c2ecf20Sopenharmony_ci * If we're called from the state manager thread, then assume we're 24558c2ecf20Sopenharmony_ci * already handling the RECLAIM_NEEDED and/or STATE_REVOKED. 24568c2ecf20Sopenharmony_ci * Those flags are expected to remain set until we're done 24578c2ecf20Sopenharmony_ci * recovering (see RFC5661, section 18.46.3). 24588c2ecf20Sopenharmony_ci */ 24598c2ecf20Sopenharmony_ci if (recovery) 24608c2ecf20Sopenharmony_ci goto out_recovery; 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED) 24638c2ecf20Sopenharmony_ci nfs41_handle_server_reboot(clp); 24648c2ecf20Sopenharmony_ci if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED)) 24658c2ecf20Sopenharmony_ci nfs41_handle_all_state_revoked(clp); 24668c2ecf20Sopenharmony_ci if (flags & (SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED | 24678c2ecf20Sopenharmony_ci SEQ4_STATUS_ADMIN_STATE_REVOKED)) 24688c2ecf20Sopenharmony_ci nfs41_handle_some_state_revoked(clp); 24698c2ecf20Sopenharmony_ci if (flags & SEQ4_STATUS_LEASE_MOVED) 24708c2ecf20Sopenharmony_ci nfs4_schedule_lease_moved_recovery(clp); 24718c2ecf20Sopenharmony_ci if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED) 24728c2ecf20Sopenharmony_ci nfs41_handle_recallable_state_revoked(clp); 24738c2ecf20Sopenharmony_ciout_recovery: 24748c2ecf20Sopenharmony_ci if (flags & SEQ4_STATUS_BACKCHANNEL_FAULT) 24758c2ecf20Sopenharmony_ci nfs41_handle_backchannel_fault(clp); 24768c2ecf20Sopenharmony_ci else if (flags & (SEQ4_STATUS_CB_PATH_DOWN | 24778c2ecf20Sopenharmony_ci SEQ4_STATUS_CB_PATH_DOWN_SESSION)) 24788c2ecf20Sopenharmony_ci nfs41_handle_cb_path_down(clp); 24798c2ecf20Sopenharmony_ci} 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_cistatic int nfs4_reset_session(struct nfs_client *clp) 24828c2ecf20Sopenharmony_ci{ 24838c2ecf20Sopenharmony_ci const struct cred *cred; 24848c2ecf20Sopenharmony_ci int status; 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci if (!nfs4_has_session(clp)) 24878c2ecf20Sopenharmony_ci return 0; 24888c2ecf20Sopenharmony_ci status = nfs4_begin_drain_session(clp); 24898c2ecf20Sopenharmony_ci if (status != 0) 24908c2ecf20Sopenharmony_ci return status; 24918c2ecf20Sopenharmony_ci cred = nfs4_get_clid_cred(clp); 24928c2ecf20Sopenharmony_ci status = nfs4_proc_destroy_session(clp->cl_session, cred); 24938c2ecf20Sopenharmony_ci switch (status) { 24948c2ecf20Sopenharmony_ci case 0: 24958c2ecf20Sopenharmony_ci case -NFS4ERR_BADSESSION: 24968c2ecf20Sopenharmony_ci case -NFS4ERR_DEADSESSION: 24978c2ecf20Sopenharmony_ci break; 24988c2ecf20Sopenharmony_ci case -NFS4ERR_BACK_CHAN_BUSY: 24998c2ecf20Sopenharmony_ci case -NFS4ERR_DELAY: 25008c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); 25018c2ecf20Sopenharmony_ci status = 0; 25028c2ecf20Sopenharmony_ci ssleep(1); 25038c2ecf20Sopenharmony_ci goto out; 25048c2ecf20Sopenharmony_ci default: 25058c2ecf20Sopenharmony_ci status = nfs4_recovery_handle_error(clp, status); 25068c2ecf20Sopenharmony_ci goto out; 25078c2ecf20Sopenharmony_ci } 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci memset(clp->cl_session->sess_id.data, 0, NFS4_MAX_SESSIONID_LEN); 25108c2ecf20Sopenharmony_ci status = nfs4_proc_create_session(clp, cred); 25118c2ecf20Sopenharmony_ci if (status) { 25128c2ecf20Sopenharmony_ci dprintk("%s: session reset failed with status %d for server %s!\n", 25138c2ecf20Sopenharmony_ci __func__, status, clp->cl_hostname); 25148c2ecf20Sopenharmony_ci status = nfs4_handle_reclaim_lease_error(clp, status); 25158c2ecf20Sopenharmony_ci goto out; 25168c2ecf20Sopenharmony_ci } 25178c2ecf20Sopenharmony_ci nfs41_finish_session_reset(clp); 25188c2ecf20Sopenharmony_ci dprintk("%s: session reset was successful for server %s!\n", 25198c2ecf20Sopenharmony_ci __func__, clp->cl_hostname); 25208c2ecf20Sopenharmony_ciout: 25218c2ecf20Sopenharmony_ci put_cred(cred); 25228c2ecf20Sopenharmony_ci return status; 25238c2ecf20Sopenharmony_ci} 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_cistatic int nfs4_bind_conn_to_session(struct nfs_client *clp) 25268c2ecf20Sopenharmony_ci{ 25278c2ecf20Sopenharmony_ci const struct cred *cred; 25288c2ecf20Sopenharmony_ci int ret; 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci if (!nfs4_has_session(clp)) 25318c2ecf20Sopenharmony_ci return 0; 25328c2ecf20Sopenharmony_ci ret = nfs4_begin_drain_session(clp); 25338c2ecf20Sopenharmony_ci if (ret != 0) 25348c2ecf20Sopenharmony_ci return ret; 25358c2ecf20Sopenharmony_ci cred = nfs4_get_clid_cred(clp); 25368c2ecf20Sopenharmony_ci ret = nfs4_proc_bind_conn_to_session(clp, cred); 25378c2ecf20Sopenharmony_ci put_cred(cred); 25388c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state); 25398c2ecf20Sopenharmony_ci switch (ret) { 25408c2ecf20Sopenharmony_ci case 0: 25418c2ecf20Sopenharmony_ci dprintk("%s: bind_conn_to_session was successful for server %s!\n", 25428c2ecf20Sopenharmony_ci __func__, clp->cl_hostname); 25438c2ecf20Sopenharmony_ci break; 25448c2ecf20Sopenharmony_ci case -NFS4ERR_DELAY: 25458c2ecf20Sopenharmony_ci ssleep(1); 25468c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state); 25478c2ecf20Sopenharmony_ci break; 25488c2ecf20Sopenharmony_ci default: 25498c2ecf20Sopenharmony_ci return nfs4_recovery_handle_error(clp, ret); 25508c2ecf20Sopenharmony_ci } 25518c2ecf20Sopenharmony_ci return 0; 25528c2ecf20Sopenharmony_ci} 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_cistatic void nfs4_layoutreturn_any_run(struct nfs_client *clp) 25558c2ecf20Sopenharmony_ci{ 25568c2ecf20Sopenharmony_ci int iomode = 0; 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4CLNT_RECALL_ANY_LAYOUT_READ, &clp->cl_state)) 25598c2ecf20Sopenharmony_ci iomode += IOMODE_READ; 25608c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4CLNT_RECALL_ANY_LAYOUT_RW, &clp->cl_state)) 25618c2ecf20Sopenharmony_ci iomode += IOMODE_RW; 25628c2ecf20Sopenharmony_ci /* Note: IOMODE_READ + IOMODE_RW == IOMODE_ANY */ 25638c2ecf20Sopenharmony_ci if (iomode) { 25648c2ecf20Sopenharmony_ci pnfs_layout_return_unused_byclid(clp, iomode); 25658c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state); 25668c2ecf20Sopenharmony_ci } 25678c2ecf20Sopenharmony_ci} 25688c2ecf20Sopenharmony_ci#else /* CONFIG_NFS_V4_1 */ 25698c2ecf20Sopenharmony_cistatic int nfs4_reset_session(struct nfs_client *clp) { return 0; } 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_cistatic int nfs4_bind_conn_to_session(struct nfs_client *clp) 25728c2ecf20Sopenharmony_ci{ 25738c2ecf20Sopenharmony_ci return 0; 25748c2ecf20Sopenharmony_ci} 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_cistatic void nfs4_layoutreturn_any_run(struct nfs_client *clp) 25778c2ecf20Sopenharmony_ci{ 25788c2ecf20Sopenharmony_ci} 25798c2ecf20Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */ 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_cistatic void nfs4_state_manager(struct nfs_client *clp) 25828c2ecf20Sopenharmony_ci{ 25838c2ecf20Sopenharmony_ci unsigned int memflags; 25848c2ecf20Sopenharmony_ci int status = 0; 25858c2ecf20Sopenharmony_ci const char *section = "", *section_sep = ""; 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci /* 25888c2ecf20Sopenharmony_ci * State recovery can deadlock if the direct reclaim code tries 25898c2ecf20Sopenharmony_ci * start NFS writeback. So ensure memory allocations are all 25908c2ecf20Sopenharmony_ci * GFP_NOFS. 25918c2ecf20Sopenharmony_ci */ 25928c2ecf20Sopenharmony_ci memflags = memalloc_nofs_save(); 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci /* Ensure exclusive access to NFSv4 state */ 25958c2ecf20Sopenharmony_ci do { 25968c2ecf20Sopenharmony_ci trace_nfs4_state_mgr(clp); 25978c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state); 25988c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) { 25998c2ecf20Sopenharmony_ci section = "purge state"; 26008c2ecf20Sopenharmony_ci status = nfs4_purge_lease(clp); 26018c2ecf20Sopenharmony_ci if (status < 0) 26028c2ecf20Sopenharmony_ci goto out_error; 26038c2ecf20Sopenharmony_ci continue; 26048c2ecf20Sopenharmony_ci } 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) { 26078c2ecf20Sopenharmony_ci section = "lease expired"; 26088c2ecf20Sopenharmony_ci /* We're going to have to re-establish a clientid */ 26098c2ecf20Sopenharmony_ci status = nfs4_reclaim_lease(clp); 26108c2ecf20Sopenharmony_ci if (status < 0) 26118c2ecf20Sopenharmony_ci goto out_error; 26128c2ecf20Sopenharmony_ci continue; 26138c2ecf20Sopenharmony_ci } 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci /* Initialize or reset the session */ 26168c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) { 26178c2ecf20Sopenharmony_ci section = "reset session"; 26188c2ecf20Sopenharmony_ci status = nfs4_reset_session(clp); 26198c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) 26208c2ecf20Sopenharmony_ci continue; 26218c2ecf20Sopenharmony_ci if (status < 0) 26228c2ecf20Sopenharmony_ci goto out_error; 26238c2ecf20Sopenharmony_ci } 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci /* Send BIND_CONN_TO_SESSION */ 26268c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, 26278c2ecf20Sopenharmony_ci &clp->cl_state)) { 26288c2ecf20Sopenharmony_ci section = "bind conn to session"; 26298c2ecf20Sopenharmony_ci status = nfs4_bind_conn_to_session(clp); 26308c2ecf20Sopenharmony_ci if (status < 0) 26318c2ecf20Sopenharmony_ci goto out_error; 26328c2ecf20Sopenharmony_ci continue; 26338c2ecf20Sopenharmony_ci } 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) { 26368c2ecf20Sopenharmony_ci section = "check lease"; 26378c2ecf20Sopenharmony_ci status = nfs4_check_lease(clp); 26388c2ecf20Sopenharmony_ci if (status < 0) 26398c2ecf20Sopenharmony_ci goto out_error; 26408c2ecf20Sopenharmony_ci continue; 26418c2ecf20Sopenharmony_ci } 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4CLNT_MOVED, &clp->cl_state)) { 26448c2ecf20Sopenharmony_ci section = "migration"; 26458c2ecf20Sopenharmony_ci status = nfs4_handle_migration(clp); 26468c2ecf20Sopenharmony_ci if (status < 0) 26478c2ecf20Sopenharmony_ci goto out_error; 26488c2ecf20Sopenharmony_ci } 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state)) { 26518c2ecf20Sopenharmony_ci section = "lease moved"; 26528c2ecf20Sopenharmony_ci status = nfs4_handle_lease_moved(clp); 26538c2ecf20Sopenharmony_ci if (status < 0) 26548c2ecf20Sopenharmony_ci goto out_error; 26558c2ecf20Sopenharmony_ci } 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_ci /* First recover reboot state... */ 26588c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) { 26598c2ecf20Sopenharmony_ci section = "reclaim reboot"; 26608c2ecf20Sopenharmony_ci status = nfs4_do_reclaim(clp, 26618c2ecf20Sopenharmony_ci clp->cl_mvops->reboot_recovery_ops); 26628c2ecf20Sopenharmony_ci if (status == -EAGAIN) 26638c2ecf20Sopenharmony_ci continue; 26648c2ecf20Sopenharmony_ci if (status < 0) 26658c2ecf20Sopenharmony_ci goto out_error; 26668c2ecf20Sopenharmony_ci nfs4_state_end_reclaim_reboot(clp); 26678c2ecf20Sopenharmony_ci continue; 26688c2ecf20Sopenharmony_ci } 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_ci /* Detect expired delegations... */ 26718c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4CLNT_DELEGATION_EXPIRED, &clp->cl_state)) { 26728c2ecf20Sopenharmony_ci section = "detect expired delegations"; 26738c2ecf20Sopenharmony_ci nfs_reap_expired_delegations(clp); 26748c2ecf20Sopenharmony_ci continue; 26758c2ecf20Sopenharmony_ci } 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci /* Now recover expired state... */ 26788c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) { 26798c2ecf20Sopenharmony_ci section = "reclaim nograce"; 26808c2ecf20Sopenharmony_ci status = nfs4_do_reclaim(clp, 26818c2ecf20Sopenharmony_ci clp->cl_mvops->nograce_recovery_ops); 26828c2ecf20Sopenharmony_ci if (status == -EAGAIN) 26838c2ecf20Sopenharmony_ci continue; 26848c2ecf20Sopenharmony_ci if (status < 0) 26858c2ecf20Sopenharmony_ci goto out_error; 26868c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state); 26878c2ecf20Sopenharmony_ci } 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_ci memalloc_nofs_restore(memflags); 26908c2ecf20Sopenharmony_ci nfs4_end_drain_session(clp); 26918c2ecf20Sopenharmony_ci nfs4_clear_state_manager_bit(clp); 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) && 26948c2ecf20Sopenharmony_ci !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, 26958c2ecf20Sopenharmony_ci &clp->cl_state)) { 26968c2ecf20Sopenharmony_ci memflags = memalloc_nofs_save(); 26978c2ecf20Sopenharmony_ci continue; 26988c2ecf20Sopenharmony_ci } 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci if (!test_and_set_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state)) { 27018c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) { 27028c2ecf20Sopenharmony_ci nfs_client_return_marked_delegations(clp); 27038c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state); 27048c2ecf20Sopenharmony_ci } 27058c2ecf20Sopenharmony_ci nfs4_layoutreturn_any_run(clp); 27068c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state); 27078c2ecf20Sopenharmony_ci } 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci return; 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci } while (refcount_read(&clp->cl_count) > 1 && !signalled()); 27128c2ecf20Sopenharmony_ci goto out_drain; 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ciout_error: 27158c2ecf20Sopenharmony_ci if (strlen(section)) 27168c2ecf20Sopenharmony_ci section_sep = ": "; 27178c2ecf20Sopenharmony_ci trace_nfs4_state_mgr_failed(clp, section, status); 27188c2ecf20Sopenharmony_ci pr_warn_ratelimited("NFS: state manager%s%s failed on NFSv4 server %s" 27198c2ecf20Sopenharmony_ci " with error %d\n", section_sep, section, 27208c2ecf20Sopenharmony_ci clp->cl_hostname, -status); 27218c2ecf20Sopenharmony_ci ssleep(1); 27228c2ecf20Sopenharmony_ciout_drain: 27238c2ecf20Sopenharmony_ci memalloc_nofs_restore(memflags); 27248c2ecf20Sopenharmony_ci nfs4_end_drain_session(clp); 27258c2ecf20Sopenharmony_ci nfs4_clear_state_manager_bit(clp); 27268c2ecf20Sopenharmony_ci} 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_cistatic int nfs4_run_state_manager(void *ptr) 27298c2ecf20Sopenharmony_ci{ 27308c2ecf20Sopenharmony_ci struct nfs_client *clp = ptr; 27318c2ecf20Sopenharmony_ci struct rpc_clnt *cl = clp->cl_rpcclient; 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci while (cl != cl->cl_parent) 27348c2ecf20Sopenharmony_ci cl = cl->cl_parent; 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_ci allow_signal(SIGKILL); 27378c2ecf20Sopenharmony_ciagain: 27388c2ecf20Sopenharmony_ci nfs4_state_manager(clp); 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) && 27418c2ecf20Sopenharmony_ci !test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) { 27428c2ecf20Sopenharmony_ci wait_var_event_interruptible(&clp->cl_state, 27438c2ecf20Sopenharmony_ci test_bit(NFS4CLNT_RUN_MANAGER, 27448c2ecf20Sopenharmony_ci &clp->cl_state)); 27458c2ecf20Sopenharmony_ci if (!atomic_read(&cl->cl_swapper)) 27468c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state); 27478c2ecf20Sopenharmony_ci if (refcount_read(&clp->cl_count) > 1 && !signalled() && 27488c2ecf20Sopenharmony_ci !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) 27498c2ecf20Sopenharmony_ci goto again; 27508c2ecf20Sopenharmony_ci /* Either no longer a swapper, or were signalled */ 27518c2ecf20Sopenharmony_ci clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state); 27528c2ecf20Sopenharmony_ci } 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci if (refcount_read(&clp->cl_count) > 1 && !signalled() && 27558c2ecf20Sopenharmony_ci test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) && 27568c2ecf20Sopenharmony_ci !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) 27578c2ecf20Sopenharmony_ci goto again; 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci nfs_put_client(clp); 27608c2ecf20Sopenharmony_ci module_put_and_exit(0); 27618c2ecf20Sopenharmony_ci return 0; 27628c2ecf20Sopenharmony_ci} 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci/* 27658c2ecf20Sopenharmony_ci * Local variables: 27668c2ecf20Sopenharmony_ci * c-basic-offset: 8 27678c2ecf20Sopenharmony_ci * End: 27688c2ecf20Sopenharmony_ci */ 2769