162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 462306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/nfs_fs.h> 862306a36Sopenharmony_ci#include <linux/nfs_mount.h> 962306a36Sopenharmony_ci#include <linux/sunrpc/addr.h> 1062306a36Sopenharmony_ci#include <linux/sunrpc/auth.h> 1162306a36Sopenharmony_ci#include <linux/sunrpc/xprt.h> 1262306a36Sopenharmony_ci#include <linux/sunrpc/bc_xprt.h> 1362306a36Sopenharmony_ci#include <linux/sunrpc/rpc_pipe_fs.h> 1462306a36Sopenharmony_ci#include "internal.h" 1562306a36Sopenharmony_ci#include "callback.h" 1662306a36Sopenharmony_ci#include "delegation.h" 1762306a36Sopenharmony_ci#include "nfs4session.h" 1862306a36Sopenharmony_ci#include "nfs4idmap.h" 1962306a36Sopenharmony_ci#include "pnfs.h" 2062306a36Sopenharmony_ci#include "netns.h" 2162306a36Sopenharmony_ci#include "sysfs.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_CLIENT 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * Get a unique NFSv4.0 callback identifier which will be used 2762306a36Sopenharmony_ci * by the V4.0 callback service to lookup the nfs_client struct 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistatic int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci int ret = 0; 3262306a36Sopenharmony_ci struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (clp->rpc_ops->version != 4 || minorversion != 0) 3562306a36Sopenharmony_ci return ret; 3662306a36Sopenharmony_ci idr_preload(GFP_KERNEL); 3762306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 3862306a36Sopenharmony_ci ret = idr_alloc(&nn->cb_ident_idr, clp, 1, 0, GFP_NOWAIT); 3962306a36Sopenharmony_ci if (ret >= 0) 4062306a36Sopenharmony_ci clp->cl_cb_ident = ret; 4162306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 4262306a36Sopenharmony_ci idr_preload_end(); 4362306a36Sopenharmony_ci return ret < 0 ? ret : 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_1 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * Per auth flavor data server rpc clients 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistruct nfs4_ds_server { 5162306a36Sopenharmony_ci struct list_head list; /* ds_clp->cl_ds_clients */ 5262306a36Sopenharmony_ci struct rpc_clnt *rpc_clnt; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * nfs4_find_ds_client - Common lookup case for DS I/O 5762306a36Sopenharmony_ci * @ds_clp: pointer to the DS's nfs_client 5862306a36Sopenharmony_ci * @flavor: rpc auth flavour to match 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_cistatic struct nfs4_ds_server * 6162306a36Sopenharmony_cinfs4_find_ds_client(struct nfs_client *ds_clp, rpc_authflavor_t flavor) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct nfs4_ds_server *dss; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci rcu_read_lock(); 6662306a36Sopenharmony_ci list_for_each_entry_rcu(dss, &ds_clp->cl_ds_clients, list) { 6762306a36Sopenharmony_ci if (dss->rpc_clnt->cl_auth->au_flavor != flavor) 6862306a36Sopenharmony_ci continue; 6962306a36Sopenharmony_ci goto out; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci dss = NULL; 7262306a36Sopenharmony_ciout: 7362306a36Sopenharmony_ci rcu_read_unlock(); 7462306a36Sopenharmony_ci return dss; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct nfs4_ds_server * 7862306a36Sopenharmony_cinfs4_add_ds_client(struct nfs_client *ds_clp, rpc_authflavor_t flavor, 7962306a36Sopenharmony_ci struct nfs4_ds_server *new) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct nfs4_ds_server *dss; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci spin_lock(&ds_clp->cl_lock); 8462306a36Sopenharmony_ci list_for_each_entry(dss, &ds_clp->cl_ds_clients, list) { 8562306a36Sopenharmony_ci if (dss->rpc_clnt->cl_auth->au_flavor != flavor) 8662306a36Sopenharmony_ci continue; 8762306a36Sopenharmony_ci goto out; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci if (new) 9062306a36Sopenharmony_ci list_add_rcu(&new->list, &ds_clp->cl_ds_clients); 9162306a36Sopenharmony_ci dss = new; 9262306a36Sopenharmony_ciout: 9362306a36Sopenharmony_ci spin_unlock(&ds_clp->cl_lock); /* need some lock to protect list */ 9462306a36Sopenharmony_ci return dss; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic struct nfs4_ds_server * 9862306a36Sopenharmony_cinfs4_alloc_ds_server(struct nfs_client *ds_clp, rpc_authflavor_t flavor) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct nfs4_ds_server *dss; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci dss = kmalloc(sizeof(*dss), GFP_NOFS); 10362306a36Sopenharmony_ci if (dss == NULL) 10462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci dss->rpc_clnt = rpc_clone_client_set_auth(ds_clp->cl_rpcclient, flavor); 10762306a36Sopenharmony_ci if (IS_ERR(dss->rpc_clnt)) { 10862306a36Sopenharmony_ci int err = PTR_ERR(dss->rpc_clnt); 10962306a36Sopenharmony_ci kfree (dss); 11062306a36Sopenharmony_ci return ERR_PTR(err); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci INIT_LIST_HEAD(&dss->list); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return dss; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void 11862306a36Sopenharmony_cinfs4_free_ds_server(struct nfs4_ds_server *dss) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci rpc_release_client(dss->rpc_clnt); 12162306a36Sopenharmony_ci kfree(dss); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * nfs4_find_or_create_ds_client - Find or create a DS rpc client 12662306a36Sopenharmony_ci * @ds_clp: pointer to the DS's nfs_client 12762306a36Sopenharmony_ci * @inode: pointer to the inode 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * Find or create a DS rpc client with th MDS server rpc client auth flavor 13062306a36Sopenharmony_ci * in the nfs_client cl_ds_clients list. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_cistruct rpc_clnt * 13362306a36Sopenharmony_cinfs4_find_or_create_ds_client(struct nfs_client *ds_clp, struct inode *inode) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct nfs4_ds_server *dss, *new; 13662306a36Sopenharmony_ci rpc_authflavor_t flavor = NFS_SERVER(inode)->client->cl_auth->au_flavor; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci dss = nfs4_find_ds_client(ds_clp, flavor); 13962306a36Sopenharmony_ci if (dss != NULL) 14062306a36Sopenharmony_ci goto out; 14162306a36Sopenharmony_ci new = nfs4_alloc_ds_server(ds_clp, flavor); 14262306a36Sopenharmony_ci if (IS_ERR(new)) 14362306a36Sopenharmony_ci return ERR_CAST(new); 14462306a36Sopenharmony_ci dss = nfs4_add_ds_client(ds_clp, flavor, new); 14562306a36Sopenharmony_ci if (dss != new) 14662306a36Sopenharmony_ci nfs4_free_ds_server(new); 14762306a36Sopenharmony_ciout: 14862306a36Sopenharmony_ci return dss->rpc_clnt; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_find_or_create_ds_client); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void 15362306a36Sopenharmony_cinfs4_shutdown_ds_clients(struct nfs_client *clp) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct nfs4_ds_server *dss; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci while (!list_empty(&clp->cl_ds_clients)) { 15862306a36Sopenharmony_ci dss = list_entry(clp->cl_ds_clients.next, 15962306a36Sopenharmony_ci struct nfs4_ds_server, list); 16062306a36Sopenharmony_ci list_del(&dss->list); 16162306a36Sopenharmony_ci rpc_shutdown_client(dss->rpc_clnt); 16262306a36Sopenharmony_ci kfree (dss); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void 16762306a36Sopenharmony_cinfs4_cleanup_callback(struct nfs_client *clp) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct nfs4_copy_state *cp_state; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci while (!list_empty(&clp->pending_cb_stateids)) { 17262306a36Sopenharmony_ci cp_state = list_entry(clp->pending_cb_stateids.next, 17362306a36Sopenharmony_ci struct nfs4_copy_state, copies); 17462306a36Sopenharmony_ci list_del(&cp_state->copies); 17562306a36Sopenharmony_ci kfree(cp_state); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_civoid nfs41_shutdown_client(struct nfs_client *clp) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci if (nfs4_has_session(clp)) { 18262306a36Sopenharmony_ci nfs4_cleanup_callback(clp); 18362306a36Sopenharmony_ci nfs4_shutdown_ds_clients(clp); 18462306a36Sopenharmony_ci nfs4_destroy_session(clp->cl_session); 18562306a36Sopenharmony_ci nfs4_destroy_clientid(clp); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_civoid nfs40_shutdown_client(struct nfs_client *clp) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci if (clp->cl_slot_tbl) { 19462306a36Sopenharmony_ci nfs4_shutdown_slot_table(clp->cl_slot_tbl); 19562306a36Sopenharmony_ci kfree(clp->cl_slot_tbl); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistruct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci char buf[INET6_ADDRSTRLEN + 1]; 20262306a36Sopenharmony_ci const char *ip_addr = cl_init->ip_addr; 20362306a36Sopenharmony_ci struct nfs_client *clp = nfs_alloc_client(cl_init); 20462306a36Sopenharmony_ci int err; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (IS_ERR(clp)) 20762306a36Sopenharmony_ci return clp; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci err = nfs_get_cb_ident_idr(clp, cl_init->minorversion); 21062306a36Sopenharmony_ci if (err) 21162306a36Sopenharmony_ci goto error; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (cl_init->minorversion > NFS4_MAX_MINOR_VERSION) { 21462306a36Sopenharmony_ci err = -EINVAL; 21562306a36Sopenharmony_ci goto error; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci spin_lock_init(&clp->cl_lock); 21962306a36Sopenharmony_ci INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); 22062306a36Sopenharmony_ci INIT_LIST_HEAD(&clp->cl_ds_clients); 22162306a36Sopenharmony_ci rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); 22262306a36Sopenharmony_ci clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; 22362306a36Sopenharmony_ci clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; 22462306a36Sopenharmony_ci clp->cl_mig_gen = 1; 22562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4_1) 22662306a36Sopenharmony_ci init_waitqueue_head(&clp->cl_lock_waitq); 22762306a36Sopenharmony_ci#endif 22862306a36Sopenharmony_ci INIT_LIST_HEAD(&clp->pending_cb_stateids); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (cl_init->minorversion != 0) 23162306a36Sopenharmony_ci __set_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags); 23262306a36Sopenharmony_ci __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags); 23362306a36Sopenharmony_ci __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (test_bit(NFS_CS_DS, &cl_init->init_flags)) 23662306a36Sopenharmony_ci __set_bit(NFS_CS_DS, &clp->cl_flags); 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * Set up the connection to the server before we add add to the 23962306a36Sopenharmony_ci * global list. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci err = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_GSS_KRB5I); 24262306a36Sopenharmony_ci if (err == -EINVAL) 24362306a36Sopenharmony_ci err = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_UNIX); 24462306a36Sopenharmony_ci if (err < 0) 24562306a36Sopenharmony_ci goto error; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* If no clientaddr= option was specified, find a usable cb address */ 24862306a36Sopenharmony_ci if (ip_addr == NULL) { 24962306a36Sopenharmony_ci struct sockaddr_storage cb_addr; 25062306a36Sopenharmony_ci struct sockaddr *sap = (struct sockaddr *)&cb_addr; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci err = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr)); 25362306a36Sopenharmony_ci if (err < 0) 25462306a36Sopenharmony_ci goto error; 25562306a36Sopenharmony_ci err = rpc_ntop(sap, buf, sizeof(buf)); 25662306a36Sopenharmony_ci if (err < 0) 25762306a36Sopenharmony_ci goto error; 25862306a36Sopenharmony_ci ip_addr = (const char *)buf; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci strscpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci err = nfs_idmap_new(clp); 26362306a36Sopenharmony_ci if (err < 0) { 26462306a36Sopenharmony_ci dprintk("%s: failed to create idmapper. Error = %d\n", 26562306a36Sopenharmony_ci __func__, err); 26662306a36Sopenharmony_ci goto error; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); 26962306a36Sopenharmony_ci return clp; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cierror: 27262306a36Sopenharmony_ci nfs_free_client(clp); 27362306a36Sopenharmony_ci return ERR_PTR(err); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/* 27762306a36Sopenharmony_ci * Destroy the NFS4 callback service 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_cistatic void nfs4_destroy_callback(struct nfs_client *clp) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 28262306a36Sopenharmony_ci nfs_callback_down(clp->cl_mvops->minor_version, clp->cl_net); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic void nfs4_shutdown_client(struct nfs_client *clp) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) 28862306a36Sopenharmony_ci nfs4_kill_renewd(clp); 28962306a36Sopenharmony_ci clp->cl_mvops->shutdown_client(clp); 29062306a36Sopenharmony_ci nfs4_destroy_callback(clp); 29162306a36Sopenharmony_ci if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) 29262306a36Sopenharmony_ci nfs_idmap_delete(clp); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci rpc_destroy_wait_queue(&clp->cl_rpcwaitq); 29562306a36Sopenharmony_ci kfree(clp->cl_serverowner); 29662306a36Sopenharmony_ci kfree(clp->cl_serverscope); 29762306a36Sopenharmony_ci kfree(clp->cl_implid); 29862306a36Sopenharmony_ci kfree(clp->cl_owner_id); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_civoid nfs4_free_client(struct nfs_client *clp) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci nfs4_shutdown_client(clp); 30462306a36Sopenharmony_ci nfs_free_client(clp); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/* 30862306a36Sopenharmony_ci * Initialize the NFS4 callback service 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_cistatic int nfs4_init_callback(struct nfs_client *clp) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct rpc_xprt *xprt; 31362306a36Sopenharmony_ci int error; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (nfs4_has_session(clp)) { 31862306a36Sopenharmony_ci error = xprt_setup_backchannel(xprt, NFS41_BC_MIN_CALLBACKS); 31962306a36Sopenharmony_ci if (error < 0) 32062306a36Sopenharmony_ci return error; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci error = nfs_callback_up(clp->cl_mvops->minor_version, xprt); 32462306a36Sopenharmony_ci if (error < 0) { 32562306a36Sopenharmony_ci dprintk("%s: failed to start callback. Error = %d\n", 32662306a36Sopenharmony_ci __func__, error); 32762306a36Sopenharmony_ci return error; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return 0; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/** 33562306a36Sopenharmony_ci * nfs40_init_client - nfs_client initialization tasks for NFSv4.0 33662306a36Sopenharmony_ci * @clp: nfs_client to initialize 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * Returns zero on success, or a negative errno if some error occurred. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ciint nfs40_init_client(struct nfs_client *clp) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct nfs4_slot_table *tbl; 34362306a36Sopenharmony_ci int ret; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci tbl = kzalloc(sizeof(*tbl), GFP_NOFS); 34662306a36Sopenharmony_ci if (tbl == NULL) 34762306a36Sopenharmony_ci return -ENOMEM; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = nfs4_setup_slot_table(tbl, NFS4_MAX_SLOT_TABLE, 35062306a36Sopenharmony_ci "NFSv4.0 transport Slot table"); 35162306a36Sopenharmony_ci if (ret) { 35262306a36Sopenharmony_ci nfs4_shutdown_slot_table(tbl); 35362306a36Sopenharmony_ci kfree(tbl); 35462306a36Sopenharmony_ci return ret; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci clp->cl_slot_tbl = tbl; 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci#if defined(CONFIG_NFS_V4_1) 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/** 36462306a36Sopenharmony_ci * nfs41_init_client - nfs_client initialization tasks for NFSv4.1+ 36562306a36Sopenharmony_ci * @clp: nfs_client to initialize 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * Returns zero on success, or a negative errno if some error occurred. 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ciint nfs41_init_client(struct nfs_client *clp) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct nfs4_session *session = NULL; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* 37462306a36Sopenharmony_ci * Create the session and mark it expired. 37562306a36Sopenharmony_ci * When a SEQUENCE operation encounters the expired session 37662306a36Sopenharmony_ci * it will do session recovery to initialize it. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci session = nfs4_alloc_session(clp); 37962306a36Sopenharmony_ci if (!session) 38062306a36Sopenharmony_ci return -ENOMEM; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci clp->cl_session = session; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* 38562306a36Sopenharmony_ci * The create session reply races with the server back 38662306a36Sopenharmony_ci * channel probe. Mark the client NFS_CS_SESSION_INITING 38762306a36Sopenharmony_ci * so that the client back channel can find the 38862306a36Sopenharmony_ci * nfs_client struct 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING); 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */ 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/* 39762306a36Sopenharmony_ci * Initialize the minor version specific parts of an NFS4 client record 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_cistatic int nfs4_init_client_minor_version(struct nfs_client *clp) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci int ret; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci ret = clp->cl_mvops->init_client(clp); 40462306a36Sopenharmony_ci if (ret) 40562306a36Sopenharmony_ci return ret; 40662306a36Sopenharmony_ci return nfs4_init_callback(clp); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic void nfs4_add_trunk(struct nfs_client *clp, struct nfs_client *old) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct sockaddr_storage clp_addr, old_addr; 41262306a36Sopenharmony_ci struct sockaddr *clp_sap = (struct sockaddr *)&clp_addr; 41362306a36Sopenharmony_ci struct sockaddr *old_sap = (struct sockaddr *)&old_addr; 41462306a36Sopenharmony_ci size_t clp_salen; 41562306a36Sopenharmony_ci struct xprt_create xprt_args = { 41662306a36Sopenharmony_ci .ident = old->cl_proto, 41762306a36Sopenharmony_ci .net = old->cl_net, 41862306a36Sopenharmony_ci .servername = old->cl_hostname, 41962306a36Sopenharmony_ci }; 42062306a36Sopenharmony_ci int max_connect = test_bit(NFS_CS_PNFS, &clp->cl_flags) ? 42162306a36Sopenharmony_ci clp->cl_max_connect : old->cl_max_connect; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (clp->cl_proto != old->cl_proto) 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci clp_salen = rpc_peeraddr(clp->cl_rpcclient, clp_sap, sizeof(clp_addr)); 42662306a36Sopenharmony_ci rpc_peeraddr(old->cl_rpcclient, old_sap, sizeof(old_addr)); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (clp_addr.ss_family != old_addr.ss_family) 42962306a36Sopenharmony_ci return; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci xprt_args.dstaddr = clp_sap; 43262306a36Sopenharmony_ci xprt_args.addrlen = clp_salen; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci rpc_clnt_add_xprt(old->cl_rpcclient, &xprt_args, 43562306a36Sopenharmony_ci rpc_clnt_test_and_add_xprt, &max_connect); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci/** 43962306a36Sopenharmony_ci * nfs4_init_client - Initialise an NFS4 client record 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * @clp: nfs_client to initialise 44262306a36Sopenharmony_ci * @cl_init: pointer to nfs_client_initdata 44362306a36Sopenharmony_ci * 44462306a36Sopenharmony_ci * Returns pointer to an NFS client, or an ERR_PTR value. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_cistruct nfs_client *nfs4_init_client(struct nfs_client *clp, 44762306a36Sopenharmony_ci const struct nfs_client_initdata *cl_init) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct nfs_client *old; 45062306a36Sopenharmony_ci int error; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (clp->cl_cons_state == NFS_CS_READY) 45362306a36Sopenharmony_ci /* the client is initialised already */ 45462306a36Sopenharmony_ci return clp; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci error = nfs4_init_client_minor_version(clp); 45762306a36Sopenharmony_ci if (error < 0) 45862306a36Sopenharmony_ci goto error; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci error = nfs4_discover_server_trunking(clp, &old); 46162306a36Sopenharmony_ci if (error < 0) 46262306a36Sopenharmony_ci goto error; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (clp != old) { 46562306a36Sopenharmony_ci clp->cl_preserve_clid = true; 46662306a36Sopenharmony_ci /* 46762306a36Sopenharmony_ci * Mark the client as having failed initialization so other 46862306a36Sopenharmony_ci * processes walking the nfs_client_list in nfs_match_client() 46962306a36Sopenharmony_ci * won't try to use it. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci nfs_mark_client_ready(clp, -EPERM); 47262306a36Sopenharmony_ci if (old->cl_mvops->session_trunk) 47362306a36Sopenharmony_ci nfs4_add_trunk(clp, old); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci clear_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags); 47662306a36Sopenharmony_ci nfs_put_client(clp); 47762306a36Sopenharmony_ci return old; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cierror: 48062306a36Sopenharmony_ci nfs_mark_client_ready(clp, error); 48162306a36Sopenharmony_ci nfs_put_client(clp); 48262306a36Sopenharmony_ci return ERR_PTR(error); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci/* 48662306a36Sopenharmony_ci * SETCLIENTID just did a callback update with the callback ident in 48762306a36Sopenharmony_ci * "drop," but server trunking discovery claims "drop" and "keep" are 48862306a36Sopenharmony_ci * actually the same server. Swap the callback IDs so that "keep" 48962306a36Sopenharmony_ci * will continue to use the callback ident the server now knows about, 49062306a36Sopenharmony_ci * and so that "keep"'s original callback ident is destroyed when 49162306a36Sopenharmony_ci * "drop" is freed. 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_cistatic void nfs4_swap_callback_idents(struct nfs_client *keep, 49462306a36Sopenharmony_ci struct nfs_client *drop) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct nfs_net *nn = net_generic(keep->cl_net, nfs_net_id); 49762306a36Sopenharmony_ci unsigned int save = keep->cl_cb_ident; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (keep->cl_cb_ident == drop->cl_cb_ident) 50062306a36Sopenharmony_ci return; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci dprintk("%s: keeping callback ident %u and dropping ident %u\n", 50362306a36Sopenharmony_ci __func__, keep->cl_cb_ident, drop->cl_cb_ident); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci idr_replace(&nn->cb_ident_idr, keep, drop->cl_cb_ident); 50862306a36Sopenharmony_ci keep->cl_cb_ident = drop->cl_cb_ident; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci idr_replace(&nn->cb_ident_idr, drop, save); 51162306a36Sopenharmony_ci drop->cl_cb_ident = save; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic bool nfs4_match_client_owner_id(const struct nfs_client *clp1, 51762306a36Sopenharmony_ci const struct nfs_client *clp2) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci if (clp1->cl_owner_id == NULL || clp2->cl_owner_id == NULL) 52062306a36Sopenharmony_ci return true; 52162306a36Sopenharmony_ci return strcmp(clp1->cl_owner_id, clp2->cl_owner_id) == 0; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic bool nfs4_same_verifier(nfs4_verifier *v1, nfs4_verifier *v2) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci return memcmp(v1->data, v2->data, sizeof(v1->data)) == 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new, 53062306a36Sopenharmony_ci struct nfs_client **prev, struct nfs_net *nn) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci int status; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (pos->rpc_ops != new->rpc_ops) 53562306a36Sopenharmony_ci return 1; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (pos->cl_minorversion != new->cl_minorversion) 53862306a36Sopenharmony_ci return 1; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* If "pos" isn't marked ready, we can't trust the 54162306a36Sopenharmony_ci * remaining fields in "pos", especially the client 54262306a36Sopenharmony_ci * ID and serverowner fields. Wait for CREATE_SESSION 54362306a36Sopenharmony_ci * to finish. */ 54462306a36Sopenharmony_ci if (pos->cl_cons_state > NFS_CS_READY) { 54562306a36Sopenharmony_ci refcount_inc(&pos->cl_count); 54662306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci nfs_put_client(*prev); 54962306a36Sopenharmony_ci *prev = pos; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci status = nfs_wait_client_init_complete(pos); 55262306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (status < 0) 55562306a36Sopenharmony_ci return status; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (pos->cl_cons_state != NFS_CS_READY) 55962306a36Sopenharmony_ci return 1; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (pos->cl_clientid != new->cl_clientid) 56262306a36Sopenharmony_ci return 1; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* NFSv4.1 always uses the uniform string, however someone 56562306a36Sopenharmony_ci * might switch the uniquifier string on us. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci if (!nfs4_match_client_owner_id(pos, new)) 56862306a36Sopenharmony_ci return 1; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/** 57462306a36Sopenharmony_ci * nfs40_walk_client_list - Find server that recognizes a client ID 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * @new: nfs_client with client ID to test 57762306a36Sopenharmony_ci * @result: OUT: found nfs_client, or new 57862306a36Sopenharmony_ci * @cred: credential to use for trunking test 57962306a36Sopenharmony_ci * 58062306a36Sopenharmony_ci * Returns zero, a negative errno, or a negative NFS4ERR status. 58162306a36Sopenharmony_ci * If zero is returned, an nfs_client pointer is planted in "result." 58262306a36Sopenharmony_ci * 58362306a36Sopenharmony_ci * NB: nfs40_walk_client_list() relies on the new nfs_client being 58462306a36Sopenharmony_ci * the last nfs_client on the list. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_ciint nfs40_walk_client_list(struct nfs_client *new, 58762306a36Sopenharmony_ci struct nfs_client **result, 58862306a36Sopenharmony_ci const struct cred *cred) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); 59162306a36Sopenharmony_ci struct nfs_client *pos, *prev = NULL; 59262306a36Sopenharmony_ci struct nfs4_setclientid_res clid = { 59362306a36Sopenharmony_ci .clientid = new->cl_clientid, 59462306a36Sopenharmony_ci .confirm = new->cl_confirm, 59562306a36Sopenharmony_ci }; 59662306a36Sopenharmony_ci int status = -NFS4ERR_STALE_CLIENTID; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 59962306a36Sopenharmony_ci list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (pos == new) 60262306a36Sopenharmony_ci goto found; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci status = nfs4_match_client(pos, new, &prev, nn); 60562306a36Sopenharmony_ci if (status < 0) 60662306a36Sopenharmony_ci goto out_unlock; 60762306a36Sopenharmony_ci if (status != 0) 60862306a36Sopenharmony_ci continue; 60962306a36Sopenharmony_ci /* 61062306a36Sopenharmony_ci * We just sent a new SETCLIENTID, which should have 61162306a36Sopenharmony_ci * caused the server to return a new cl_confirm. So if 61262306a36Sopenharmony_ci * cl_confirm is the same, then this is a different 61362306a36Sopenharmony_ci * server that just returned the same cl_confirm by 61462306a36Sopenharmony_ci * coincidence: 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci if ((new != pos) && nfs4_same_verifier(&pos->cl_confirm, 61762306a36Sopenharmony_ci &new->cl_confirm)) 61862306a36Sopenharmony_ci continue; 61962306a36Sopenharmony_ci /* 62062306a36Sopenharmony_ci * But if the cl_confirm's are different, then the only 62162306a36Sopenharmony_ci * way that a SETCLIENTID_CONFIRM to pos can succeed is 62262306a36Sopenharmony_ci * if new and pos point to the same server: 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_cifound: 62562306a36Sopenharmony_ci refcount_inc(&pos->cl_count); 62662306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci nfs_put_client(prev); 62962306a36Sopenharmony_ci prev = pos; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci status = nfs4_proc_setclientid_confirm(pos, &clid, cred); 63262306a36Sopenharmony_ci switch (status) { 63362306a36Sopenharmony_ci case -NFS4ERR_STALE_CLIENTID: 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci case 0: 63662306a36Sopenharmony_ci nfs4_swap_callback_idents(pos, new); 63762306a36Sopenharmony_ci pos->cl_confirm = new->cl_confirm; 63862306a36Sopenharmony_ci nfs_mark_client_ready(pos, NFS_CS_READY); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci prev = NULL; 64162306a36Sopenharmony_ci *result = pos; 64262306a36Sopenharmony_ci goto out; 64362306a36Sopenharmony_ci case -ERESTARTSYS: 64462306a36Sopenharmony_ci case -ETIMEDOUT: 64562306a36Sopenharmony_ci /* The callback path may have been inadvertently 64662306a36Sopenharmony_ci * changed. Schedule recovery! 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_ci nfs4_schedule_path_down_recovery(pos); 64962306a36Sopenharmony_ci goto out; 65062306a36Sopenharmony_ci default: 65162306a36Sopenharmony_ci goto out; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ciout_unlock: 65762306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* No match found. The server lost our clientid */ 66062306a36Sopenharmony_ciout: 66162306a36Sopenharmony_ci nfs_put_client(prev); 66262306a36Sopenharmony_ci return status; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_1 66662306a36Sopenharmony_ci/* 66762306a36Sopenharmony_ci * Returns true if the server major ids match 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_cibool 67062306a36Sopenharmony_cinfs4_check_serverowner_major_id(struct nfs41_server_owner *o1, 67162306a36Sopenharmony_ci struct nfs41_server_owner *o2) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci if (o1->major_id_sz != o2->major_id_sz) 67462306a36Sopenharmony_ci return false; 67562306a36Sopenharmony_ci return memcmp(o1->major_id, o2->major_id, o1->major_id_sz) == 0; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci/* 67962306a36Sopenharmony_ci * Returns true if the server scopes match 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_cistatic bool 68262306a36Sopenharmony_cinfs4_check_server_scope(struct nfs41_server_scope *s1, 68362306a36Sopenharmony_ci struct nfs41_server_scope *s2) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci if (s1->server_scope_sz != s2->server_scope_sz) 68662306a36Sopenharmony_ci return false; 68762306a36Sopenharmony_ci return memcmp(s1->server_scope, s2->server_scope, 68862306a36Sopenharmony_ci s1->server_scope_sz) == 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci/** 69262306a36Sopenharmony_ci * nfs4_detect_session_trunking - Checks for session trunking. 69362306a36Sopenharmony_ci * @clp: original mount nfs_client 69462306a36Sopenharmony_ci * @res: result structure from an exchange_id using the original mount 69562306a36Sopenharmony_ci * nfs_client with a new multi_addr transport 69662306a36Sopenharmony_ci * @xprt: pointer to the transport to add. 69762306a36Sopenharmony_ci * 69862306a36Sopenharmony_ci * Called after a successful EXCHANGE_ID on a multi-addr connection. 69962306a36Sopenharmony_ci * Upon success, add the transport. 70062306a36Sopenharmony_ci * 70162306a36Sopenharmony_ci * Returns zero on success, otherwise -EINVAL 70262306a36Sopenharmony_ci * 70362306a36Sopenharmony_ci * Note: since the exchange_id for the new multi_addr transport uses the 70462306a36Sopenharmony_ci * same nfs_client from the original mount, the cl_owner_id is reused, 70562306a36Sopenharmony_ci * so eir_clientowner is the same. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ciint nfs4_detect_session_trunking(struct nfs_client *clp, 70862306a36Sopenharmony_ci struct nfs41_exchange_id_res *res, 70962306a36Sopenharmony_ci struct rpc_xprt *xprt) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci /* Check eir_clientid */ 71262306a36Sopenharmony_ci if (clp->cl_clientid != res->clientid) 71362306a36Sopenharmony_ci goto out_err; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* Check eir_server_owner so_major_id */ 71662306a36Sopenharmony_ci if (!nfs4_check_serverowner_major_id(clp->cl_serverowner, 71762306a36Sopenharmony_ci res->server_owner)) 71862306a36Sopenharmony_ci goto out_err; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Check eir_server_owner so_minor_id */ 72162306a36Sopenharmony_ci if (clp->cl_serverowner->minor_id != res->server_owner->minor_id) 72262306a36Sopenharmony_ci goto out_err; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* Check eir_server_scope */ 72562306a36Sopenharmony_ci if (!nfs4_check_server_scope(clp->cl_serverscope, res->server_scope)) 72662306a36Sopenharmony_ci goto out_err; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci pr_info("NFS: %s: Session trunking succeeded for %s\n", 72962306a36Sopenharmony_ci clp->cl_hostname, 73062306a36Sopenharmony_ci xprt->address_strings[RPC_DISPLAY_ADDR]); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ciout_err: 73462306a36Sopenharmony_ci pr_info("NFS: %s: Session trunking failed for %s\n", clp->cl_hostname, 73562306a36Sopenharmony_ci xprt->address_strings[RPC_DISPLAY_ADDR]); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return -EINVAL; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci/** 74162306a36Sopenharmony_ci * nfs41_walk_client_list - Find nfs_client that matches a client/server owner 74262306a36Sopenharmony_ci * 74362306a36Sopenharmony_ci * @new: nfs_client with client ID to test 74462306a36Sopenharmony_ci * @result: OUT: found nfs_client, or new 74562306a36Sopenharmony_ci * @cred: credential to use for trunking test 74662306a36Sopenharmony_ci * 74762306a36Sopenharmony_ci * Returns zero, a negative errno, or a negative NFS4ERR status. 74862306a36Sopenharmony_ci * If zero is returned, an nfs_client pointer is planted in "result." 74962306a36Sopenharmony_ci * 75062306a36Sopenharmony_ci * NB: nfs41_walk_client_list() relies on the new nfs_client being 75162306a36Sopenharmony_ci * the last nfs_client on the list. 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ciint nfs41_walk_client_list(struct nfs_client *new, 75462306a36Sopenharmony_ci struct nfs_client **result, 75562306a36Sopenharmony_ci const struct cred *cred) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); 75862306a36Sopenharmony_ci struct nfs_client *pos, *prev = NULL; 75962306a36Sopenharmony_ci int status = -NFS4ERR_STALE_CLIENTID; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 76262306a36Sopenharmony_ci list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (pos == new) 76562306a36Sopenharmony_ci goto found; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci status = nfs4_match_client(pos, new, &prev, nn); 76862306a36Sopenharmony_ci if (status < 0) 76962306a36Sopenharmony_ci goto out; 77062306a36Sopenharmony_ci if (status != 0) 77162306a36Sopenharmony_ci continue; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* 77462306a36Sopenharmony_ci * Note that session trunking is just a special subcase of 77562306a36Sopenharmony_ci * client id trunking. In either case, we want to fall back 77662306a36Sopenharmony_ci * to using the existing nfs_client. 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_ci if (!nfs4_check_serverowner_major_id(pos->cl_serverowner, 77962306a36Sopenharmony_ci new->cl_serverowner)) 78062306a36Sopenharmony_ci continue; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cifound: 78362306a36Sopenharmony_ci refcount_inc(&pos->cl_count); 78462306a36Sopenharmony_ci *result = pos; 78562306a36Sopenharmony_ci status = 0; 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ciout: 79062306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 79162306a36Sopenharmony_ci nfs_put_client(prev); 79262306a36Sopenharmony_ci return status; 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */ 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic void nfs4_destroy_server(struct nfs_server *server) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci LIST_HEAD(freeme); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci nfs_server_return_all_delegations(server); 80162306a36Sopenharmony_ci unset_pnfs_layoutdriver(server); 80262306a36Sopenharmony_ci nfs4_purge_state_owners(server, &freeme); 80362306a36Sopenharmony_ci nfs4_free_state_owners(&freeme); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci/* 80762306a36Sopenharmony_ci * NFSv4.0 callback thread helper 80862306a36Sopenharmony_ci * 80962306a36Sopenharmony_ci * Find a client by callback identifier 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_cistruct nfs_client * 81262306a36Sopenharmony_cinfs4_find_client_ident(struct net *net, int cb_ident) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct nfs_client *clp; 81562306a36Sopenharmony_ci struct nfs_net *nn = net_generic(net, nfs_net_id); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 81862306a36Sopenharmony_ci clp = idr_find(&nn->cb_ident_idr, cb_ident); 81962306a36Sopenharmony_ci if (clp) 82062306a36Sopenharmony_ci refcount_inc(&clp->cl_count); 82162306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 82262306a36Sopenharmony_ci return clp; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci#if defined(CONFIG_NFS_V4_1) 82662306a36Sopenharmony_ci/* Common match routine for v4.0 and v4.1 callback services */ 82762306a36Sopenharmony_cistatic bool nfs4_cb_match_client(const struct sockaddr *addr, 82862306a36Sopenharmony_ci struct nfs_client *clp, u32 minorversion) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* Don't match clients that failed to initialise */ 83362306a36Sopenharmony_ci if (!(clp->cl_cons_state == NFS_CS_READY || 83462306a36Sopenharmony_ci clp->cl_cons_state == NFS_CS_SESSION_INITING)) 83562306a36Sopenharmony_ci return false; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci smp_rmb(); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci /* Match the version and minorversion */ 84062306a36Sopenharmony_ci if (clp->rpc_ops->version != 4 || 84162306a36Sopenharmony_ci clp->cl_minorversion != minorversion) 84262306a36Sopenharmony_ci return false; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* Match only the IP address, not the port number */ 84562306a36Sopenharmony_ci return rpc_cmp_addr(addr, clap); 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci/* 84962306a36Sopenharmony_ci * NFSv4.1 callback thread helper 85062306a36Sopenharmony_ci * For CB_COMPOUND calls, find a client by IP address, protocol version, 85162306a36Sopenharmony_ci * minorversion, and sessionID 85262306a36Sopenharmony_ci * 85362306a36Sopenharmony_ci * Returns NULL if no such client 85462306a36Sopenharmony_ci */ 85562306a36Sopenharmony_cistruct nfs_client * 85662306a36Sopenharmony_cinfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, 85762306a36Sopenharmony_ci struct nfs4_sessionid *sid, u32 minorversion) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct nfs_client *clp; 86062306a36Sopenharmony_ci struct nfs_net *nn = net_generic(net, nfs_net_id); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 86362306a36Sopenharmony_ci list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { 86462306a36Sopenharmony_ci if (!nfs4_cb_match_client(addr, clp, minorversion)) 86562306a36Sopenharmony_ci continue; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (!nfs4_has_session(clp)) 86862306a36Sopenharmony_ci continue; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci /* Match sessionid*/ 87162306a36Sopenharmony_ci if (memcmp(clp->cl_session->sess_id.data, 87262306a36Sopenharmony_ci sid->data, NFS4_MAX_SESSIONID_LEN) != 0) 87362306a36Sopenharmony_ci continue; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci refcount_inc(&clp->cl_count); 87662306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 87762306a36Sopenharmony_ci return clp; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 88062306a36Sopenharmony_ci return NULL; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci#else /* CONFIG_NFS_V4_1 */ 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistruct nfs_client * 88662306a36Sopenharmony_cinfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, 88762306a36Sopenharmony_ci struct nfs4_sessionid *sid, u32 minorversion) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci return NULL; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */ 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci/* 89462306a36Sopenharmony_ci * Set up an NFS4 client 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_cistatic int nfs4_set_client(struct nfs_server *server, 89762306a36Sopenharmony_ci const char *hostname, 89862306a36Sopenharmony_ci const struct sockaddr_storage *addr, 89962306a36Sopenharmony_ci const size_t addrlen, 90062306a36Sopenharmony_ci const char *ip_addr, 90162306a36Sopenharmony_ci int proto, const struct rpc_timeout *timeparms, 90262306a36Sopenharmony_ci u32 minorversion, unsigned int nconnect, 90362306a36Sopenharmony_ci unsigned int max_connect, 90462306a36Sopenharmony_ci struct net *net, 90562306a36Sopenharmony_ci struct xprtsec_parms *xprtsec) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct nfs_client_initdata cl_init = { 90862306a36Sopenharmony_ci .hostname = hostname, 90962306a36Sopenharmony_ci .addr = addr, 91062306a36Sopenharmony_ci .addrlen = addrlen, 91162306a36Sopenharmony_ci .ip_addr = ip_addr, 91262306a36Sopenharmony_ci .nfs_mod = &nfs_v4, 91362306a36Sopenharmony_ci .proto = proto, 91462306a36Sopenharmony_ci .minorversion = minorversion, 91562306a36Sopenharmony_ci .net = net, 91662306a36Sopenharmony_ci .timeparms = timeparms, 91762306a36Sopenharmony_ci .cred = server->cred, 91862306a36Sopenharmony_ci .xprtsec = *xprtsec, 91962306a36Sopenharmony_ci }; 92062306a36Sopenharmony_ci struct nfs_client *clp; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (minorversion == 0) 92362306a36Sopenharmony_ci __set_bit(NFS_CS_REUSEPORT, &cl_init.init_flags); 92462306a36Sopenharmony_ci else 92562306a36Sopenharmony_ci cl_init.max_connect = max_connect; 92662306a36Sopenharmony_ci switch (proto) { 92762306a36Sopenharmony_ci case XPRT_TRANSPORT_TCP: 92862306a36Sopenharmony_ci case XPRT_TRANSPORT_TCP_TLS: 92962306a36Sopenharmony_ci cl_init.nconnect = nconnect; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (server->flags & NFS_MOUNT_NORESVPORT) 93362306a36Sopenharmony_ci __set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); 93462306a36Sopenharmony_ci if (server->options & NFS_OPTION_MIGRATION) 93562306a36Sopenharmony_ci __set_bit(NFS_CS_MIGRATION, &cl_init.init_flags); 93662306a36Sopenharmony_ci if (test_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status)) 93762306a36Sopenharmony_ci __set_bit(NFS_CS_TSM_POSSIBLE, &cl_init.init_flags); 93862306a36Sopenharmony_ci server->port = rpc_get_port((struct sockaddr *)addr); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* Allocate or find a client reference we can use */ 94162306a36Sopenharmony_ci clp = nfs_get_client(&cl_init); 94262306a36Sopenharmony_ci if (IS_ERR(clp)) 94362306a36Sopenharmony_ci return PTR_ERR(clp); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci if (server->nfs_client == clp) { 94662306a36Sopenharmony_ci nfs_put_client(clp); 94762306a36Sopenharmony_ci return -ELOOP; 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* 95162306a36Sopenharmony_ci * Query for the lease time on clientid setup or renewal 95262306a36Sopenharmony_ci * 95362306a36Sopenharmony_ci * Note that this will be set on nfs_clients that were created 95462306a36Sopenharmony_ci * only for the DS role and did not set this bit, but now will 95562306a36Sopenharmony_ci * serve a dual role. 95662306a36Sopenharmony_ci */ 95762306a36Sopenharmony_ci set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci server->nfs_client = clp; 96062306a36Sopenharmony_ci nfs_sysfs_add_server(server); 96162306a36Sopenharmony_ci nfs_sysfs_link_rpc_client(server, clp->cl_rpcclient, "_state"); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci return 0; 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/* 96762306a36Sopenharmony_ci * Set up a pNFS Data Server client. 96862306a36Sopenharmony_ci * 96962306a36Sopenharmony_ci * Return any existing nfs_client that matches server address,port,version 97062306a36Sopenharmony_ci * and minorversion. 97162306a36Sopenharmony_ci * 97262306a36Sopenharmony_ci * For a new nfs_client, use a soft mount (default), a low retrans and a 97362306a36Sopenharmony_ci * low timeout interval so that if a connection is lost, we retry through 97462306a36Sopenharmony_ci * the MDS. 97562306a36Sopenharmony_ci */ 97662306a36Sopenharmony_cistruct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv, 97762306a36Sopenharmony_ci const struct sockaddr_storage *ds_addr, int ds_addrlen, 97862306a36Sopenharmony_ci int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans, 97962306a36Sopenharmony_ci u32 minor_version) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct rpc_timeout ds_timeout; 98262306a36Sopenharmony_ci struct nfs_client *mds_clp = mds_srv->nfs_client; 98362306a36Sopenharmony_ci struct nfs_client_initdata cl_init = { 98462306a36Sopenharmony_ci .addr = ds_addr, 98562306a36Sopenharmony_ci .addrlen = ds_addrlen, 98662306a36Sopenharmony_ci .nodename = mds_clp->cl_rpcclient->cl_nodename, 98762306a36Sopenharmony_ci .ip_addr = mds_clp->cl_ipaddr, 98862306a36Sopenharmony_ci .nfs_mod = &nfs_v4, 98962306a36Sopenharmony_ci .proto = ds_proto, 99062306a36Sopenharmony_ci .minorversion = minor_version, 99162306a36Sopenharmony_ci .net = mds_clp->cl_net, 99262306a36Sopenharmony_ci .timeparms = &ds_timeout, 99362306a36Sopenharmony_ci .cred = mds_srv->cred, 99462306a36Sopenharmony_ci .xprtsec = mds_srv->nfs_client->cl_xprtsec, 99562306a36Sopenharmony_ci }; 99662306a36Sopenharmony_ci char buf[INET6_ADDRSTRLEN + 1]; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (rpc_ntop((struct sockaddr *)ds_addr, buf, sizeof(buf)) <= 0) 99962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 100062306a36Sopenharmony_ci cl_init.hostname = buf; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci switch (ds_proto) { 100362306a36Sopenharmony_ci case XPRT_TRANSPORT_TCP: 100462306a36Sopenharmony_ci case XPRT_TRANSPORT_TCP_TLS: 100562306a36Sopenharmony_ci if (mds_clp->cl_nconnect > 1) { 100662306a36Sopenharmony_ci cl_init.nconnect = mds_clp->cl_nconnect; 100762306a36Sopenharmony_ci cl_init.max_connect = NFS_MAX_TRANSPORTS; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (mds_srv->flags & NFS_MOUNT_NORESVPORT) 101262306a36Sopenharmony_ci __set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci __set_bit(NFS_CS_DS, &cl_init.init_flags); 101562306a36Sopenharmony_ci __set_bit(NFS_CS_PNFS, &cl_init.init_flags); 101662306a36Sopenharmony_ci cl_init.max_connect = NFS_MAX_TRANSPORTS; 101762306a36Sopenharmony_ci /* 101862306a36Sopenharmony_ci * Set an authflavor equual to the MDS value. Use the MDS nfs_client 101962306a36Sopenharmony_ci * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS 102062306a36Sopenharmony_ci * (section 13.1 RFC 5661). 102162306a36Sopenharmony_ci */ 102262306a36Sopenharmony_ci nfs_init_timeout_values(&ds_timeout, ds_proto, ds_timeo, ds_retrans); 102362306a36Sopenharmony_ci return nfs_get_client(&cl_init); 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_set_ds_client); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci/* 102862306a36Sopenharmony_ci * Session has been established, and the client marked ready. 102962306a36Sopenharmony_ci * Limit the mount rsize, wsize and dtsize using negotiated fore 103062306a36Sopenharmony_ci * channel attributes. 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_cistatic void nfs4_session_limit_rwsize(struct nfs_server *server) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_1 103562306a36Sopenharmony_ci struct nfs4_session *sess; 103662306a36Sopenharmony_ci u32 server_resp_sz; 103762306a36Sopenharmony_ci u32 server_rqst_sz; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci if (!nfs4_has_session(server->nfs_client)) 104062306a36Sopenharmony_ci return; 104162306a36Sopenharmony_ci sess = server->nfs_client->cl_session; 104262306a36Sopenharmony_ci server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead; 104362306a36Sopenharmony_ci server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci if (server->dtsize > server_resp_sz) 104662306a36Sopenharmony_ci server->dtsize = server_resp_sz; 104762306a36Sopenharmony_ci if (server->rsize > server_resp_sz) 104862306a36Sopenharmony_ci server->rsize = server_resp_sz; 104962306a36Sopenharmony_ci if (server->wsize > server_rqst_sz) 105062306a36Sopenharmony_ci server->wsize = server_rqst_sz; 105162306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */ 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci/* 105562306a36Sopenharmony_ci * Limit xattr sizes using the channel attributes. 105662306a36Sopenharmony_ci */ 105762306a36Sopenharmony_cistatic void nfs4_session_limit_xasize(struct nfs_server *server) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2 106062306a36Sopenharmony_ci struct nfs4_session *sess; 106162306a36Sopenharmony_ci u32 server_gxa_sz; 106262306a36Sopenharmony_ci u32 server_sxa_sz; 106362306a36Sopenharmony_ci u32 server_lxa_sz; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (!nfs4_has_session(server->nfs_client)) 106662306a36Sopenharmony_ci return; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci sess = server->nfs_client->cl_session; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci server_gxa_sz = sess->fc_attrs.max_resp_sz - nfs42_maxgetxattr_overhead; 107162306a36Sopenharmony_ci server_sxa_sz = sess->fc_attrs.max_rqst_sz - nfs42_maxsetxattr_overhead; 107262306a36Sopenharmony_ci server_lxa_sz = sess->fc_attrs.max_resp_sz - 107362306a36Sopenharmony_ci nfs42_maxlistxattrs_overhead; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (server->gxasize > server_gxa_sz) 107662306a36Sopenharmony_ci server->gxasize = server_gxa_sz; 107762306a36Sopenharmony_ci if (server->sxasize > server_sxa_sz) 107862306a36Sopenharmony_ci server->sxasize = server_sxa_sz; 107962306a36Sopenharmony_ci if (server->lxasize > server_lxa_sz) 108062306a36Sopenharmony_ci server->lxasize = server_lxa_sz; 108162306a36Sopenharmony_ci#endif 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_civoid nfs4_server_set_init_caps(struct nfs_server *server) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci /* Set the basic capabilities */ 108762306a36Sopenharmony_ci server->caps |= server->nfs_client->cl_mvops->init_caps; 108862306a36Sopenharmony_ci if (server->flags & NFS_MOUNT_NORDIRPLUS) 108962306a36Sopenharmony_ci server->caps &= ~NFS_CAP_READDIRPLUS; 109062306a36Sopenharmony_ci if (server->nfs_client->cl_proto == XPRT_TRANSPORT_RDMA) 109162306a36Sopenharmony_ci server->caps &= ~NFS_CAP_READ_PLUS; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* 109462306a36Sopenharmony_ci * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower 109562306a36Sopenharmony_ci * authentication. 109662306a36Sopenharmony_ci */ 109762306a36Sopenharmony_ci if (nfs4_disable_idmapping && 109862306a36Sopenharmony_ci server->client->cl_auth->au_flavor == RPC_AUTH_UNIX) 109962306a36Sopenharmony_ci server->caps |= NFS_CAP_UIDGID_NOMAP; 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_cistatic int nfs4_server_common_setup(struct nfs_server *server, 110362306a36Sopenharmony_ci struct nfs_fh *mntfh, bool auth_probe) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci int error; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci /* data servers support only a subset of NFSv4.1 */ 110862306a36Sopenharmony_ci if (is_ds_only_client(server->nfs_client)) 110962306a36Sopenharmony_ci return -EPROTONOSUPPORT; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci /* We must ensure the session is initialised first */ 111262306a36Sopenharmony_ci error = nfs4_init_session(server->nfs_client); 111362306a36Sopenharmony_ci if (error < 0) 111462306a36Sopenharmony_ci goto out; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci nfs4_server_set_init_caps(server); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* Probe the root fh to retrieve its FSID and filehandle */ 111962306a36Sopenharmony_ci error = nfs4_get_rootfh(server, mntfh, auth_probe); 112062306a36Sopenharmony_ci if (error < 0) 112162306a36Sopenharmony_ci goto out; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci dprintk("Server FSID: %llx:%llx\n", 112462306a36Sopenharmony_ci (unsigned long long) server->fsid.major, 112562306a36Sopenharmony_ci (unsigned long long) server->fsid.minor); 112662306a36Sopenharmony_ci nfs_display_fhandle(mntfh, "Pseudo-fs root FH"); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci error = nfs_probe_server(server, mntfh); 112962306a36Sopenharmony_ci if (error < 0) 113062306a36Sopenharmony_ci goto out; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci nfs4_session_limit_rwsize(server); 113362306a36Sopenharmony_ci nfs4_session_limit_xasize(server); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 113662306a36Sopenharmony_ci server->namelen = NFS4_MAXNAMLEN; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci nfs_server_insert_lists(server); 113962306a36Sopenharmony_ci server->mount_time = jiffies; 114062306a36Sopenharmony_ci server->destroy = nfs4_destroy_server; 114162306a36Sopenharmony_ciout: 114262306a36Sopenharmony_ci return error; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci/* 114662306a36Sopenharmony_ci * Create a version 4 volume record 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_cistatic int nfs4_init_server(struct nfs_server *server, struct fs_context *fc) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci struct nfs_fs_context *ctx = nfs_fc2context(fc); 115162306a36Sopenharmony_ci struct rpc_timeout timeparms; 115262306a36Sopenharmony_ci int error; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci nfs_init_timeout_values(&timeparms, ctx->nfs_server.protocol, 115562306a36Sopenharmony_ci ctx->timeo, ctx->retrans); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci /* Initialise the client representation from the mount data */ 115862306a36Sopenharmony_ci server->flags = ctx->flags; 115962306a36Sopenharmony_ci server->options = ctx->options; 116062306a36Sopenharmony_ci server->auth_info = ctx->auth_info; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci /* Use the first specified auth flavor. If this flavor isn't 116362306a36Sopenharmony_ci * allowed by the server, use the SECINFO path to try the 116462306a36Sopenharmony_ci * other specified flavors */ 116562306a36Sopenharmony_ci if (ctx->auth_info.flavor_len >= 1) 116662306a36Sopenharmony_ci ctx->selected_flavor = ctx->auth_info.flavors[0]; 116762306a36Sopenharmony_ci else 116862306a36Sopenharmony_ci ctx->selected_flavor = RPC_AUTH_UNIX; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci /* Get a client record */ 117162306a36Sopenharmony_ci error = nfs4_set_client(server, 117262306a36Sopenharmony_ci ctx->nfs_server.hostname, 117362306a36Sopenharmony_ci &ctx->nfs_server._address, 117462306a36Sopenharmony_ci ctx->nfs_server.addrlen, 117562306a36Sopenharmony_ci ctx->client_address, 117662306a36Sopenharmony_ci ctx->nfs_server.protocol, 117762306a36Sopenharmony_ci &timeparms, 117862306a36Sopenharmony_ci ctx->minorversion, 117962306a36Sopenharmony_ci ctx->nfs_server.nconnect, 118062306a36Sopenharmony_ci ctx->nfs_server.max_connect, 118162306a36Sopenharmony_ci fc->net_ns, 118262306a36Sopenharmony_ci &ctx->xprtsec); 118362306a36Sopenharmony_ci if (error < 0) 118462306a36Sopenharmony_ci return error; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (ctx->rsize) 118762306a36Sopenharmony_ci server->rsize = nfs_io_size(ctx->rsize, server->nfs_client->cl_proto); 118862306a36Sopenharmony_ci if (ctx->wsize) 118962306a36Sopenharmony_ci server->wsize = nfs_io_size(ctx->wsize, server->nfs_client->cl_proto); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci server->acregmin = ctx->acregmin * HZ; 119262306a36Sopenharmony_ci server->acregmax = ctx->acregmax * HZ; 119362306a36Sopenharmony_ci server->acdirmin = ctx->acdirmin * HZ; 119462306a36Sopenharmony_ci server->acdirmax = ctx->acdirmax * HZ; 119562306a36Sopenharmony_ci server->port = ctx->nfs_server.port; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci return nfs_init_server_rpcclient(server, &timeparms, 119862306a36Sopenharmony_ci ctx->selected_flavor); 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci/* 120262306a36Sopenharmony_ci * Create a version 4 volume record 120362306a36Sopenharmony_ci * - keyed on server and FSID 120462306a36Sopenharmony_ci */ 120562306a36Sopenharmony_cistruct nfs_server *nfs4_create_server(struct fs_context *fc) 120662306a36Sopenharmony_ci{ 120762306a36Sopenharmony_ci struct nfs_fs_context *ctx = nfs_fc2context(fc); 120862306a36Sopenharmony_ci struct nfs_server *server; 120962306a36Sopenharmony_ci bool auth_probe; 121062306a36Sopenharmony_ci int error; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci server = nfs_alloc_server(); 121362306a36Sopenharmony_ci if (!server) 121462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci server->cred = get_cred(fc->cred); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci auth_probe = ctx->auth_info.flavor_len < 1; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci /* set up the general RPC client */ 122162306a36Sopenharmony_ci error = nfs4_init_server(server, fc); 122262306a36Sopenharmony_ci if (error < 0) 122362306a36Sopenharmony_ci goto error; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci error = nfs4_server_common_setup(server, ctx->mntfh, auth_probe); 122662306a36Sopenharmony_ci if (error < 0) 122762306a36Sopenharmony_ci goto error; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci return server; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_cierror: 123262306a36Sopenharmony_ci nfs_free_server(server); 123362306a36Sopenharmony_ci return ERR_PTR(error); 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci/* 123762306a36Sopenharmony_ci * Create an NFS4 referral server record 123862306a36Sopenharmony_ci */ 123962306a36Sopenharmony_cistruct nfs_server *nfs4_create_referral_server(struct fs_context *fc) 124062306a36Sopenharmony_ci{ 124162306a36Sopenharmony_ci struct nfs_fs_context *ctx = nfs_fc2context(fc); 124262306a36Sopenharmony_ci struct nfs_client *parent_client; 124362306a36Sopenharmony_ci struct nfs_server *server, *parent_server; 124462306a36Sopenharmony_ci int proto, error; 124562306a36Sopenharmony_ci bool auth_probe; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci server = nfs_alloc_server(); 124862306a36Sopenharmony_ci if (!server) 124962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci parent_server = NFS_SB(ctx->clone_data.sb); 125262306a36Sopenharmony_ci parent_client = parent_server->nfs_client; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci server->cred = get_cred(parent_server->cred); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* Initialise the client representation from the parent server */ 125762306a36Sopenharmony_ci nfs_server_copy_userdata(server, parent_server); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci /* Get a client representation */ 126062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) 126162306a36Sopenharmony_ci rpc_set_port(&ctx->nfs_server.address, NFS_RDMA_PORT); 126262306a36Sopenharmony_ci error = nfs4_set_client(server, 126362306a36Sopenharmony_ci ctx->nfs_server.hostname, 126462306a36Sopenharmony_ci &ctx->nfs_server._address, 126562306a36Sopenharmony_ci ctx->nfs_server.addrlen, 126662306a36Sopenharmony_ci parent_client->cl_ipaddr, 126762306a36Sopenharmony_ci XPRT_TRANSPORT_RDMA, 126862306a36Sopenharmony_ci parent_server->client->cl_timeout, 126962306a36Sopenharmony_ci parent_client->cl_mvops->minor_version, 127062306a36Sopenharmony_ci parent_client->cl_nconnect, 127162306a36Sopenharmony_ci parent_client->cl_max_connect, 127262306a36Sopenharmony_ci parent_client->cl_net, 127362306a36Sopenharmony_ci &parent_client->cl_xprtsec); 127462306a36Sopenharmony_ci if (!error) 127562306a36Sopenharmony_ci goto init_server; 127662306a36Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */ 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci proto = XPRT_TRANSPORT_TCP; 127962306a36Sopenharmony_ci if (parent_client->cl_xprtsec.policy != RPC_XPRTSEC_NONE) 128062306a36Sopenharmony_ci proto = XPRT_TRANSPORT_TCP_TLS; 128162306a36Sopenharmony_ci rpc_set_port(&ctx->nfs_server.address, NFS_PORT); 128262306a36Sopenharmony_ci error = nfs4_set_client(server, 128362306a36Sopenharmony_ci ctx->nfs_server.hostname, 128462306a36Sopenharmony_ci &ctx->nfs_server._address, 128562306a36Sopenharmony_ci ctx->nfs_server.addrlen, 128662306a36Sopenharmony_ci parent_client->cl_ipaddr, 128762306a36Sopenharmony_ci proto, 128862306a36Sopenharmony_ci parent_server->client->cl_timeout, 128962306a36Sopenharmony_ci parent_client->cl_mvops->minor_version, 129062306a36Sopenharmony_ci parent_client->cl_nconnect, 129162306a36Sopenharmony_ci parent_client->cl_max_connect, 129262306a36Sopenharmony_ci parent_client->cl_net, 129362306a36Sopenharmony_ci &parent_client->cl_xprtsec); 129462306a36Sopenharmony_ci if (error < 0) 129562306a36Sopenharmony_ci goto error; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) 129862306a36Sopenharmony_ciinit_server: 129962306a36Sopenharmony_ci#endif 130062306a36Sopenharmony_ci error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, 130162306a36Sopenharmony_ci ctx->selected_flavor); 130262306a36Sopenharmony_ci if (error < 0) 130362306a36Sopenharmony_ci goto error; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci auth_probe = parent_server->auth_info.flavor_len < 1; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci error = nfs4_server_common_setup(server, ctx->mntfh, auth_probe); 130862306a36Sopenharmony_ci if (error < 0) 130962306a36Sopenharmony_ci goto error; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci return server; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_cierror: 131462306a36Sopenharmony_ci nfs_free_server(server); 131562306a36Sopenharmony_ci return ERR_PTR(error); 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci/** 131962306a36Sopenharmony_ci * nfs4_update_server - Move an nfs_server to a different nfs_client 132062306a36Sopenharmony_ci * 132162306a36Sopenharmony_ci * @server: represents FSID to be moved 132262306a36Sopenharmony_ci * @hostname: new end-point's hostname 132362306a36Sopenharmony_ci * @sap: new end-point's socket address 132462306a36Sopenharmony_ci * @salen: size of "sap" 132562306a36Sopenharmony_ci * @net: net namespace 132662306a36Sopenharmony_ci * 132762306a36Sopenharmony_ci * The nfs_server must be quiescent before this function is invoked. 132862306a36Sopenharmony_ci * Either its session is drained (NFSv4.1+), or its transport is 132962306a36Sopenharmony_ci * plugged and drained (NFSv4.0). 133062306a36Sopenharmony_ci * 133162306a36Sopenharmony_ci * Returns zero on success, or a negative errno value. 133262306a36Sopenharmony_ci */ 133362306a36Sopenharmony_ciint nfs4_update_server(struct nfs_server *server, const char *hostname, 133462306a36Sopenharmony_ci struct sockaddr_storage *sap, size_t salen, struct net *net) 133562306a36Sopenharmony_ci{ 133662306a36Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 133762306a36Sopenharmony_ci struct rpc_clnt *clnt = server->client; 133862306a36Sopenharmony_ci struct xprt_create xargs = { 133962306a36Sopenharmony_ci .ident = clp->cl_proto, 134062306a36Sopenharmony_ci .net = net, 134162306a36Sopenharmony_ci .dstaddr = (struct sockaddr *)sap, 134262306a36Sopenharmony_ci .addrlen = salen, 134362306a36Sopenharmony_ci .servername = hostname, 134462306a36Sopenharmony_ci /* cel: bleh. We might need to pass TLS parameters here */ 134562306a36Sopenharmony_ci }; 134662306a36Sopenharmony_ci char buf[INET6_ADDRSTRLEN + 1]; 134762306a36Sopenharmony_ci struct sockaddr_storage address; 134862306a36Sopenharmony_ci struct sockaddr *localaddr = (struct sockaddr *)&address; 134962306a36Sopenharmony_ci int error; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci error = rpc_switch_client_transport(clnt, &xargs, clnt->cl_timeout); 135262306a36Sopenharmony_ci if (error != 0) 135362306a36Sopenharmony_ci return error; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci error = rpc_localaddr(clnt, localaddr, sizeof(address)); 135662306a36Sopenharmony_ci if (error != 0) 135762306a36Sopenharmony_ci return error; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci if (rpc_ntop(localaddr, buf, sizeof(buf)) == 0) 136062306a36Sopenharmony_ci return -EAFNOSUPPORT; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci nfs_server_remove_lists(server); 136362306a36Sopenharmony_ci set_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status); 136462306a36Sopenharmony_ci error = nfs4_set_client(server, hostname, sap, salen, buf, 136562306a36Sopenharmony_ci clp->cl_proto, clnt->cl_timeout, 136662306a36Sopenharmony_ci clp->cl_minorversion, 136762306a36Sopenharmony_ci clp->cl_nconnect, clp->cl_max_connect, 136862306a36Sopenharmony_ci net, &clp->cl_xprtsec); 136962306a36Sopenharmony_ci clear_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status); 137062306a36Sopenharmony_ci if (error != 0) { 137162306a36Sopenharmony_ci nfs_server_insert_lists(server); 137262306a36Sopenharmony_ci return error; 137362306a36Sopenharmony_ci } 137462306a36Sopenharmony_ci nfs_put_client(clp); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci if (server->nfs_client->cl_hostname == NULL) { 137762306a36Sopenharmony_ci server->nfs_client->cl_hostname = kstrdup(hostname, GFP_KERNEL); 137862306a36Sopenharmony_ci if (server->nfs_client->cl_hostname == NULL) 137962306a36Sopenharmony_ci return -ENOMEM; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci nfs_server_insert_lists(server); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci return nfs_probe_server(server, NFS_FH(d_inode(server->super->s_root))); 138462306a36Sopenharmony_ci} 1385